6

我被要求维护一些不像我想要的那样遗留的代码,并且它充满了编译器指令,使其几乎不可读并且几乎可以维护。一个例子:

#if CONDITION_1
        protected override void BeforeAdd(LogEntity entity)
#else
        protected override void BeforeAdd(AbstractBusinessEntity entity)
#endif
        {
#if CONDITON_1
            entity.DateTimeInsert = DateTime.Now;
#else
            ((LogEntity) entity).DateTimeInsert = DateTime.Now;
#endif
            base.BeforeAdd(entity);
        }

using指令更漂亮:

#if CONDITION_1
using CompanyName.Configuration;
#endif

#if CONDITION_2||CONDITION_1
using CompanyName.Data;
using CompanyName.Data.SqlBuilders;
#else
using CompanyName.Legacy.Database;
using CompanyName.Legacy.Database.SQLBuilders;
using CompanyName.Legacy.Database.SQLBuilders.parameterTypes;
#endif

我以为我会试一试,ConditionalAttribute但在这种情况下那行不通

有什么办法可以摆脱这个编译器指令的噩梦吗?

代码是针对.NET 3.5.

更新:
Oded 回答建议删除该BeforeAdd方法周围的编译器指令,从而使其重载。不幸的是,这不起作用,因为这两种方法都应该覆盖一个AbstractBusiness类,该类根据最终包含的程序集提供两种不同的实现:

protected virtual void BeforeAdd(TEntity entity) {}

或者

protected virtual void BeforeAdd(AbstractBusinessEntity entity) {}

该代码从公司过去一段时间创建的一组库中获取其依赖关系,并且从那时起一直在“升级”。他们现在拥有这组库的 4 个不同版本,具有冲突的命名空间和不同的实现。全部以使用(非常)旧版本的应用程序的“向后兼容性”的名义。


结论

我最终选择了@Oded 的答案,因为它作为一种通用方法(KISS等等)最有意义。不过在这种情况下我不能使用它;你在这里看到的只是冰山一角。如果它付钱给我,我不想亲吻这个代码。

4

4 回答 4

7

在第一种情况下,看起来您可以简单地使用该方法的多个重载而不是此构造。重载解决方案应该在这一点上解决问题。

在第二种情况下(使用指令) - 您可以为一些指令设置别名并包括所有指令,在需要时使用别名。当包含所有命名空间时会发生什么?有名字冲突吗?

于 2011-03-18T15:00:17.030 回答
2

我会声称问题不在这堂课上。这个类只是一个症状。问题出在调用 BeforeAdd 的基类中。如果您可以在那里重构,那么您将不需要条件编译。

如果您有冲突的名称和命名空间,您可以使用 using 关键字(不是用于程序集的关键字)来解决这个问题。

所以你可以做类似的事情

using LegacyLogEntity = Some.Fully.Qualified.Namespace.LogEntity;
using SomeOtherLogEntity = Some.Other.Fully.Qualified.Namespace.CurrentLogEntity;

// ..
LegacyLogEntity entity = new LegacyLogEntity();

我也认为问题出在基类,而不是这个类本身。

在那种情况下,您可以通过使用适配或接口来绕过这种废话。

我不知道另一个类叫什么,但假设它被称为 EntityAggregator。

public interface IEntity {
    DateTime InsertionTime { get; set; }
}

然后在您的聚合器基类中:

protected virtual void BeforeAdd(IEntity entity)
{ // whatever
}

然后在你的子类中:

protected override void BeforeAdd(IEntity entity)
{
    entity.DateTime = DateTime.Now;
    base.BeforeAdd(entity);
}

现在,您可以通过实现该接口将其他对象调整为 IEntity。

当我查看这段代码时,我也觉得你可能使用的是事件而不是这段代码。

现在,如果您谈论的是多用途编译,其中代码在两个不同的条件下在两个不同的位置编译,那么您可以通过使用部分类更优雅地做到这一点。

您将 CONDITION_1 代码隔离成如下内容:

// in file WhateverYourClassIs.condition1.cs
#if !CONDITION_1
#error this file should never be included in a build WITHOUT CONDITION_1 set
#endif

public partial class WhateverYourClassIs {
    protected override void BeforeAdd(LogEntity entity) {
        entity.DateTimeInsert = DateTime.Now;
        base.BeforeAdd(entity);
    }
}

// in file WhateverYourClassIs.NotCondition1.cs

#if CONDITION_1
#error this file should never be included in a build WITH CONDITION_1 set
#endif

public partial class WhateverYourClassIs {
    protected override void BeforeAdd(AbstractBusinessEntity entity) {
        ((LogEntity)entity).DateTimeInsert = DateTime.Now;
        base.BeforeAdd(entity);
    }
}

由于代码重复,我不喜欢这种情况。您可以使用 using 关键字来帮助解决这个问题:

#if CONDITION_1
using MyAbstractBusinessEntity = LogEntity;
#else
using MyAbstractBusinessEntity = AbstractBusinessEntity;
#endif

// ...

protected override void BeforeAdd(MyAbstractBusinessEntity entity)
{
    // in CONDITION_1, the case is a no-op
    ((LogEntity)entity).DateTimeInsert = DateTime.Now;
    base.BeforeAdd(entity);
}
于 2011-03-18T16:06:23.707 回答
1

根据我所看到的,原始开发人员似乎没有任何继承和多态感。从代码中很难分辨,但似乎 LogEntity 和 AbstractBusinessEntity 具有共同的属性。是否有继承模型或者它们是两个完全不相关的类?如果它们不相关,您可以创建一个继承模型或一个它们都可以实现的接口吗?如果您粘贴课程可能会有所帮助。

长话短说,我不会浪费时间尝试以当前形式使用该代码。我会不惜一切代价找到一种消除编译器指令的方法。它看起来并非完全无法挽救,但可能需要一些努力。

于 2011-03-18T15:31:53.957 回答
0

我不知道这是否实用,但我会在我的 DVCS Mercurial 中创建分支来处理这个问题。

在我修复错误/添加常见代码时,我将有 2 个分支在运行,而第 3 个分支暂时可用。

以下是我将如何创建初始版本:

              5---6---7         <-- type 1 of library
             /
1---2---3---4
             \
              8---9--10         <-- type 2 of library

仅修复其中一个中的错误:

              5---6---7--11     <-- bugfix or change only to type 1
             /
1---2---3---4
             \
              8---9--10

要修复常见的错误:

              5---6---7--11--13--15    <-- merged into type 1
             /                   /
1---2---3---4--11--12---+-------+      <-- common fix(es)
             \           \
              8---9--10--14            <-- merged into type 2

注意:这假设您不会在任何类型或公共分支中进行严厉的重构,如果您这样做,您可能会更好地适应当前的情况,至少与像这样的分支方式相比。任何这样的重构都会让未来的合并变得非常痛苦。

于 2011-03-18T16:05:20.320 回答