#if DEBUG vs.条件(“DEBUG”)

时间:2010-09-24 15:34:08

标签: c# debugging preprocessor debug-symbols

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

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

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

8 个答案:

答案 0 :(得分:542)

这实际上取决于你的目标:

  • #if DEBUG:此处的代码在发布时甚至无法到达IL。
  • [Conditional("DEBUG")]:此代码将到达IL,但是除非在编译调用者时设置了DEBUG,否则将省略对该方法的调用

我个人根据具体情况使用两者:

条件(“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,请记住编译期间省略了调用,不是运行时。那就是:

MyLibrary.dll

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

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

当库是针对发布模式编译的(即没有DEBUG符号)时,即使对B()的调用是,A()内的A()也会被忽略。包含因为DEBUG是在调用程序集中定义的。

答案 1 :(得分:61)

嗯,值得注意的是,它们根本不是一回事。

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

如果代码及其所有调用者都在同一个程序集中,那么这个差异 less 很重要 - 但这意味着在第一种情况下你需要{{ 1}}围绕调用代码。

就我个人而言,我建议采用第二种方法 - 但你确实需要保持它们之间的区别。

答案 2 :(得分:42)

我相信很多人会不同意我的看法,但是花了很多时间作为一个不停地听到“但它适用于我的机器!”的构建人员,我认为你应该从不使用它们。如果你真的需要测试和调试的东西,找出一种方法,使可测试性与实际的生产代码分开。

在单元测试中使用模拟来抽象场景,为要测试的一个场景制作一个关闭版本的东西,但是不要将调试测试放入您为生产版本测试和编写的二进制文件的代码中。这些调试测试只是隐藏了开发人员可能出现的错误,因此在此过程的后期才能找到它们。

答案 3 :(得分:12)

这个也很有用:

if (Debugger.IsAttached)
{
...
}

答案 4 :(得分:9)

使用第一个示例,如果未定义SetPrivateValue,则构建中将不存在DEBUG,第二个示例调用SetPrivateValue获胜如果未定义DEBUG,则存在于构建中。

在第一个示例中,您还必须使用SetPrivateValue#if DEBUG进行任何调用。

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

如果要省略调用并保存被调用者的空间,可以使用这两种技术的组合:

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

答案 5 :(得分:4)

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

假设主项目可执行文件引用的DLL中存在#if DEBUGConditional函数。使用#if,将对库的编译设置执行条件评估。使用Conditional属性,将根据调用者的编译设置执行条件评估。

答案 6 :(得分:2)

我有一个SOAP WebService扩展来使用自定义[TraceExtension]记录网络流量。我仅将此用于Debug构建,并从Release构建中省略。使用#if DEBUG包装[TraceExtension]属性,从而将其从Release版本中删除。

#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(...)

答案 7 :(得分:0)

通常,您需要在Program.cs中使用它,以便在其中决定对“非调试”代码运行“调试”,而多数情况下则在Windows Services中运行。因此,我创建了一个只读字段IsDebugMode并在静态构造函数中设置其值,如下所示。

valueOf()