在C#中删除预编译器指令

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

#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和所有这些)。 我不能在这种情况下使用它; 你在这里看到的只是冰山一角。 如果付给我的话,我不想亲密这个代码。

在第一种情况下,看起来你可能只是简单地有几个方法的重载而不是这个构造。 此时,重载解决方案应该处理好事情。

在第二种情况下(使用指令) – 您可以使用别名在需要的地方为一些指令添加别名并包含所有指令。 包含所有名称空间后会发生什么? 有没有名字碰撞?

我声称问题不在这个课程中。 这个class只是一个症状。 问题出在调用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); } 

基于我所看到的,原始开发人员似乎没有任何inheritance和多态感。 从代码中说出来有点困难,但似乎LogEntity和AbstractBusinessEntity共享公共属性。 是inheritance模型还是两个完全不相关的类? 如果它们不相关,您可以创建一个inheritance模型或它们都可以实现的接口吗? 如果您粘贴课程可能会有所帮助。

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

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

我会有两个分支在玩,第三个临时,我修复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 

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