【发布时间】:2018-07-13 12:27:53
【问题描述】:
我正在探索 C# 中高级条件编译的可能性。我的工作案例是我需要根据项目的配置有不同的行为。我不是说平庸的DEBUG/RELEASE。
例如,这是我的代码:
namespace Common
{
public struct ActionResult
{
int id;
string summary;
}
public class Widget
{
public ActionResult Process(out string path)
{
// A few lines of code
path = "path_to_file";
var result = DoAction(path);
// A few lines of code
return result;
}
protected virtual ActionResult DoAction(string path)
{
#if RELEASE_TARGET_1
HelperForTarget_1();
#elif RELEASE_TARGET_2
// Do other action...
#endif
#if TRACE_TO_CONSOLE
return new ConsoleLogger(new ActionResult());
#else
return new ActionResult();
#endif
}
[Conditional("RELEASE_TARGET_1")]
private void HelperForTarget_1()
{
// Initializing a pair or triple of class fields.
}
}
}
首先,#if/#endif 看起来很丑。每个人都知道自动重命名在排除区域不起作用(我知道 ReSharper 可以,但仍然如此)。代码看起来像是用碎片缝制的皮革脸。难以感知,难以支撑;
其次,ConditionalAttribute 不能总是应用于方法。例如,一个方法可以返回一个值或有 out 参数。一种选择是将DoAction 拆分为一堆小方法,并仍然将ConditionalAttribute 应用于它们。但是,为了补偿返回值和输出参数,您将不得不使类复杂化,其中包含仅几个实现之一需要的字段。字段可能必须包装#if/#endif,WTF!
第三,多态性。提取包含所有实现的一般行为的抽象WidgetBase,并创建具体版本,例如实现特定行为的WidgetForTarget1、WidgetForTarget2 等。听起来不错,但是具体的小部件不能继承类,只能继承接口。有时它从根本上是不可接受的。如果我提取接口IWidget,那么具体的实现将包含大量的复制粘贴——这很糟糕而且很难维护,不错的尝试。
我开始寻找partial 类/方法,希望有解决方法,但没有。有一个丑陋的方法:将依赖编译的代码提取到partial类的不同文件中,然后包装整个文件#if/#endif,但这就是我要摆脱的丑陋,该死!
我想过编译模块 (.netmodule) 但实际上它是汇编,只是没有清单。
我正在考虑使用发布者策略等进行程序集绑定(重定向版本),但无论如何,这并不是将政治家用于其他目的,此外,它仍然难以维护。
总之,我绝望了,拒绝相信这种工作没有好办法。值得注意的是,在 C++ 中,这解决起来非常简单:我可以在头文件中声明类,然后在一个 .cpp 文件中实现一般行为,在其他 cpp 文件中实现与配置相关的行为并应用这些 .cpp文件编译取决于我的设置解决方案。
【问题讨论】:
-
条件编译并不是你真正可以用来组织项目的东西。正如您已经描述的那样,它不会扩展。为什么不使用
static方法,即程序组织?这将是一个进步。 -
@AluanHaddad,很抱歉,但当您谈论使用
static方法时,我不太明白您的意思。我在static方法中看不到出路。我想要您的观点的详细信息,提前谢谢您。 -
我只是说你应该保持简单。继承是 C# 中的许多组织技术之一。您可以编写检查条件然后采取相应措施的静态方法。方法可以通过委托作为参数传递给其他方法。方法可以基于不相关的类型进行静态调度,形成临时组织,使用扩展方法。并且有很多方法可以将它们结合起来,并将它们与多态性一起使用。另外,不要忘记您可以使用全局变量。一个坏主意?是的。比使用
#ifdef构建代码更好吗?是的。 -
@AluanHaddad,你的建议让我思考。我大致了解您的想法,这种方法与我通常的工作流程不同。感谢您的建议。
标签: c# conditional-compilation