#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只是隐藏了开发人员可能出现的错误,所以直到后来才被发现。
在第一个示例中,如果未定义DEBUG
则SetPrivateValue
将不存在于构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 DEBUG
或Conditional
函数存在于您的主项目可执行文件引用的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(...)