#if DEBUG vs. Conditional("DEBUG")

在大型项目中,使用哪种方法更好,以及为什么:

#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
327359 次浏览

好吧,值得注意的是,它们的意思完全不同。

如果DEBUG符号没有定义,那么在第一种情况下SetPrivateValue本身不会被调用…而在第二种情况下,它将存在,但任何编译时不使用DEBUG符号的调用者将省略这些调用。

如果代码及其所有调用者都在同一个程序集中,这种差异就很重要——但这意味着在第一种情况下,也需要在调用代码周围加上#if DEBUG

就我个人而言,我会推荐第二种方法——但你确实需要在脑子里清楚它们之间的区别。

对于第一个例子,如果DEBUG没有定义,SetPrivateValue将不存在于构建中;对于第二个例子,如果DEBUG没有定义,调用SetPrivateValue将不存在于构建中。

对于第一个例子,你必须用#if DEBUG来包装对SetPrivateValue的调用。

在第二个例子中,对SetPrivateValue的调用将被省略,但请注意SetPrivateValue本身仍将被编译。如果您正在构建一个库,那么引用您的库的应用程序仍然可以使用您的函数(如果条件满足),那么这很有用。

如果你想忽略调用并节省被调用者的空间,你可以结合使用这两种技术:

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}

这取决于你想要什么:

  • #if DEBUG:这里的代码在发布时甚至不会到达IL。
  • [Conditional("DEBUG")]:此代码将到达IL,但是方法的调用将被省略,除非在编译调用者时设置了DEBUG。

就我个人而言,我会根据情况使用这两种方法:

条件(“调试”)的例子:我使用这个,这样我就不必在发布期间回头编辑我的代码,但在调试期间,我想确保我没有任何拼写错误。当我试图在INotifyPropertyChanged中使用属性名时,这个函数检查我是否正确地键入了属性名。

[Conditional("DEBUG")]
[DebuggerStepThrough]
protected void VerifyPropertyName(String propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
Debug.Fail(String.Format("Invalid property name. Type: {0}, Name: {1}",
GetType(), propertyName));
}

你真的不想使用#if DEBUG创建一个函数,除非你愿意用相同的#if DEBUG包装对该函数的每次调用:

#if DEBUG
public void DoSomething() { }
#endif


public void Foo()
{
#if DEBUG
DoSomething(); //This works, but looks FUGLY
#endif
}

对比:

[Conditional("DEBUG")]
public void DoSomething() { }


public void Foo()
{
DoSomething(); //Code compiles and is cleaner, DoSomething always
//exists, however this is only called during DEBUG.
}

#if DEBUG示例:我在尝试为WCF通信设置不同的绑定时使用这个。

#if DEBUG
public const String ENDPOINT = "Localhost";
#else
public const String ENDPOINT = "BasicHttpBinding";
#endif

在第一个示例中,所有代码都存在,但除非打开DEBUG,否则将被忽略。在第二个例子中,const ENDPOINT被设置为"Localhost"或"BasicHttpBinding",这取决于是否设置DEBUG。


更新:我正在更新这个答案,以澄清一个重要而棘手的问题。如果你选择使用ConditionalAttribute,请记住在编译过程中调用会被省略,而没有运行时. c。那就是:

MyLibrary.dll

[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}


[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}

当库根据发布模式编译时(即没有DEBUG符号),它将永远省略A()中对B()的调用,即使包含了对A()的调用,因为DEBUG是在调用程序集中定义的。

让我们假设你的代码也有#else语句,它定义了一个空存根函数,解决了Jon Skeet的一个点。这两者之间还有第二个重要的区别。

假设#if DEBUGConditional函数存在于被你的主项目可执行文件引用的DLL中。使用#if,条件的求值将根据库的编译设置执行。使用Conditional属性,条件的计算将根据调用方的编译设置执行。

我相信很多人会不同意我的观点,但作为一个构建人员,我经常听到“但它在我的机器上工作!”,我的立场是,你几乎不应该使用它们。如果您确实需要一些东西来测试和调试,请找出一种方法,使可测试性与实际的生产代码分开。

在单元测试中抽象带有模拟的场景,为您想要测试的一次性场景制作一次性内容的版本,但不要将用于调试的测试放入用于测试和为生产版本编写的二进制代码中。这些调试测试只是向开发人员隐藏了可能的错误,所以直到过程的后期才会发现它们。

我有一个SOAP WebService扩展来记录网络流量使用自定义[TraceExtension]。我只在调试构建中使用它,而省略了释放构建。使用#if DEBUG来包装[TraceExtension]属性,从而将其从释放构建中移除。

#if DEBUG
[TraceExtension]
#endif
[System.Web.Service.Protocols.SoapDocumentMethodAttribute( ... )]
[ more attributes ...]
public DatabaseResponse[] GetDatabaseResponse( ...)
{
object[] results = this.Invoke("GetDatabaseResponse",new object[] {
... parmeters}};
}


#if DEBUG
[TraceExtension]
#endif
public System.IAsyncResult BeginGetDatabaseResponse(...)


#if DEBUG
[TraceExtension]
#endif
public DatabaseResponse[] EndGetDatabaseResponse(...)

这个也很有用:

if (Debugger.IsAttached)
{
...
}

通常你会在Program.cs中需要它,在这里你想要决定在非调试代码上运行调试,而在Windows服务中也是如此。因此,我创建了一个只读字段IsDebugMode,并在静态构造函数中设置其值,如下所示。

static class Program
{


#region Private variable
static readonly bool IsDebugMode = false;
#endregion Private variable


#region Constrcutors
static Program()
{
#if DEBUG
IsDebugMode = true;
#endif
}
#endregion


#region Main


/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{


if (IsDebugMode)
{
MyService myService = new MyService(args);
myService.OnDebug();
}
else
{
ServiceBase[] services = new ServiceBase[] { new MyService (args) };
services.Run(args);
}
}


#endregion Main
}