#if DEBUG与条件(“DEBUG”)

哪个更好用,为什么在一个大项目上:

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

要么

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

这真的取决于你要做什么:

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

我个人使用两个视情况而定:

有条件的(“DEBUG”)例子:我使用这个,以便在发布期间我不必回头编辑我的代码,但是在debugging过程中,我想确保我没有任何input错误。 这个函数检查我试图在我的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创build一个函数,除非你愿意用相同的#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”。


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

MyLibrary.dll

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

当库被编译为释放模式(即没有DEBUG符号)时,即使包含对A()的调用,也会从A()省略对B()的调用,因为在调用程序集中定义了DEBUG 。

那么值得一提的是,他们根本不是那个意思。

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

如果代码和所有的调用者在同一个程序集中,这个区别就不那么重要了,但是这意味着在第一种情况下,你需要在调用代码中使用#if DEBUG

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

我相信很多人会不同意我的看法,但是花了很长的时间来做一个不断听到“但是在我的机器上运行”的build设人员,我认为你几乎从不使用的立场。 如果您确实需要testing和debugging,可以找出一种方法,使testing性与实际生产代码分离。

在unit testing中抽象模拟场景,为您想要testing的场景创build一个版本,但不要将testing用于debugging,而是将代码用于testing和编写用于生产版本的二进制代码。 这些debuggingtesting只是隐藏了开发人员可能出现的错误,所以直到后来才被发现。

在第一个示例中,如果未定义DEBUGSetPrivateValue将不存在于构build中,如果未定义DEBUG则在构build中不会存在对SetPrivateValue 调用

在第一个例子中,您必须使用#if DEBUG将所有对SetPrivateValue调用包装起来。

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

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

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

让我们假设你的代码也有一个#else语句,它定义了一个空stub函数,来处理Jon Skeet的一个要点。 这两者之间还有一个重要的区别。

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

这也是有用的:

 if (Debugger.IsAttached) { ... } 

我有一个SOAP WebService扩展使用自定义的[TraceExtension]来loggingnetworkingstream量。 我只用于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(...)