我被要求保留一些不像遗产一样的代码,并且它充满了编译器指令,使其几乎不可读并且几乎可维护。一个很好的例子:
#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的答案,因为它最有意义作为一般方法(K.I.S.S.以及所有这些)。但是我不能在这种情况下使用它;你在这里看到的只是冰山一角。我不想要K.I.S.S.如果它付给我这个代码。
答案 0 :(得分:7)
在第一种情况下,看起来您可以简单地使用该方法的几个重载而不是此构造。此时,重载解决方案应该处理好事情。
在第二种情况下(使用指令) - 您可以使用所需的别名为一些指令添加别名并包含所有指令。包含所有名称空间后会发生什么?任何名称冲突?
答案 1 :(得分: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);
}
答案 2 :(得分:1)
根据我所看到的情况,原来的开发人员似乎没有任何继承感和多态性。从代码中分辨出来有点困难,但似乎LogEntity和AbstractBusinessEntity共享公共属性。是继承模型还是两个完全不相关的类?如果它们不相关,您可以创建一个继承模型或它们都可以实现的接口吗?如果你粘贴了这些课程可能会有所帮助。
长话短说,我不会浪费时间尝试以当前形式使用该代码。我会不惜一切代价找到消除编译器指令的方法。它看起来并不是完全不可挽救的,但可能需要一些努力。
答案 3 :(得分:0)
我不知道它是否实用,但我会做的是在我的DVCS Mercurial中创建分支来处理这个问题。
我会有2个分支在玩,而第3个暂时在我修复bug /添加常见的代码。
以下是我将如何创建初始版本:
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
注意:这假设您不会在任何类型或公共分支中进行严厉的重构,如果您这样做,您可能会更好地了解您当前的情况,至少与这样的分支方式相比。任何这样的重构都会使未来的合并真的痛苦。