2010-09-24 29 views
349

哪个更好用,为什么呢,一个大项目:#如果DEBUG与条件( “调试”)

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

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

参见http://blogs.msdn.com/b/ericlippert/archive/2009/09/10/what-s-the-在这个问题上的一些想法之间的差异之间的条件编译和条件属性.aspx。 – 2010-09-24 17:53:26

+0

你也可以使用这个:if(Debugger.IsAttached){...} – sofsntp 2016-09-30 12:57:44

回答

471

这真的取决于你要什么:

  • #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上的代码。在第二个示例中,取决于是否设置了DEBUG,将const ENDPOINT设置为“Localhost”或“BasicHttpBinding”。


更新:因为这个答案是最高的投票问题的答案,我更新这个答案澄清的一个重要和棘手的问题。如果您选择使用ConditionalAttribute,请记住,在编译过程中忽略调用,并且而不是运行时。那就是:

MyLibrary.dll

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

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

如果库对释放模式(即没有调试符号),它会永远有从A()中省略B()调用,即使调用编译包含A()是因为在调用程序集中定义了DEBUG。

+9

DoSomething的#if调试不需要包含#if DEBUG所包围的所有调用语句。你可以是1:只要#是DEBUG DoSomething的内部,或者,做一个空白的DoSomething定义的#else。你的评论仍然帮助我理解了它们之间的差异,但是#if DEBUG不需要像你所展示的那样丑陋。 – Apeiron 2011-10-25 19:31:12

+2

如果您只是在#if调试内容时,JIT可能仍然包含在您的代码在非调试版本中运行时对函数的调用。使用条件属性意味着JIT不知道在非DEBUG构建中何时输出该调用点。 – 2013-09-11 04:11:32

+0

有条件不排除代码。它用元数据标记它。在调用代码的编译时,DEBUG值被匹配。这可能在具有标记方法的组件内部或外部。 – 2013-09-11 14:00:14

54

嗯,这是值得注意的是,他们并不意味着同样的事情。

如果没有定义DEBUG符号,然后在第一种情况下SetPrivateValue本身不会叫......而在第二种情况下,它会存在,但任何呼叫者谁是没有调试符号编译将省略这些呼叫。

如果代码及其所有来电号码在同一装配这种差异是重要 - 但它意味着,在第一种情况下,你也需要有围绕调用代码#if DEBUG为好。

就我个人而言,我会推荐第二种方法 - 但您确实需要保持头脑中清晰的差异。

+3

对于调用代码,也需要#if语句。这意味着会有#if语句的激增...... – 2010-09-24 15:53:35

+0

虽然第二个选项(条件属性)在某些情况下更好,更清晰,但可能需要传递方法调用将从程序集中剥离的事实编译(例如通过命名约定)。 – 2014-12-18 08:16:19

9

随着第一例子中,SetPrivateValue不会在构建存在如果DEBUG没有定义,与第二示例中,调用如果没有定义DEBUGSetPrivateValue不会在构建存在。

第一个示例中,您必须将所有调用与#if DEBUG包装在一起。

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

如果你想省略呼叫和保存被叫用户的空间,你可以使用这两种技术的结合:

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

@P Daddy:围绕'Conditional(“DEBUG”)环绕'#if DEBUG'并不会移除对该函数的调用,它只是将IL中的函数全部删除,所以您仍然有调用函数,存在(编译错误)。 – 2010-09-24 16:15:11

+0

如果你不想让代码在release中存在,那么应该在“#if DEBUG”中包装方法体,可能使用“#else”存根(具有throw或者dummy返回值),并使用属性建议来电者不打扰电话?这似乎是两全其美。 – supercat 2010-09-24 17:05:05

+0

@myermian,@supercat:是的,你们都对。我的错。我将按照supercat的建议进行编辑。 – 2010-09-24 17:12:30

4

让我们假设你的代码也有其定义一个空存根功能,解决的乔恩斯基特的点之一的#else声明。这两者之间还有一个重要的区别。

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

36

我敢肯定,很多会同意我的看法,但花了时间作为构建家伙不断听到“但它的作品在我的机器!”,我带你应该几乎从来不使用任何的立场。如果你确实需要测试和调试的东西,找出一种方法来使测试性与实际生产代码分离。

摘要在单元测试中嘲笑场景,为您想要测试的场景制作一个版本的测试版本,但不要将测试用于调试,而是将代码用于测试和编写用于生产版本的二进制代码。这些调试测试只是隐藏了开发人员可能出现的错误,所以直到后来才发现它们。

+3

我完全同意你的吉米。如果你正在使用DI和嘲笑你的测试,为什么你需要'#if调试'或代码中的任何类似构造? – 2010-09-24 16:18:39

+0

@RichardEv可能有更好的方法来处理这个问题,但我目前正在使用它来允许我通过查询字符串来扮演不同用户的角色。我不想在生产环境中使用它,但我希望它能够用于调试,因此我可以控制逐步完成的工作流程,而无需创建多个用户并登录到这两个帐户以浏览流程。虽然这是我第一次真的必须使用它。 – Tony 2013-07-23 15:55:00

+3

我们经常做一些事情,比如在调试版本中使用'#if DEBUG'设置默认收件人电子邮件,这样我们就不会在测试必须传输电子邮件的系统作为处理。有时候这些工具是正确的工具:) – 2013-11-21 14:26:44

0

我有一个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(...) 
6

这一个可以是有用的,以及:

if (Debugger.IsAttached) 
{ 
... 
}