[英]#if DEBUG vs. Conditional(“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設置被省略。我個人根據情況使用兩者:
Conditional("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
,請記住在編譯期間省略調用,而不是運行時。 即:
我的圖書館.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
當庫針對發布模式(即沒有 DEBUG 符號)編譯時,它將永遠省略從A()
對B()
的調用,即使包含對A()
的調用,因為 DEBUG 是在調用程序集中定義的.
好吧,值得注意的是,它們根本不是同一個意思。
如果未定義 DEBUG 符號,則在第一種情況下將不會調用SetPrivateValue
本身……而在第二種情況下它會存在,但任何在沒有 DEBUG 符號的情況下編譯的調用者都將省略這些調用。
如果代碼和它的所有調用者都在同一個程序集中,這個區別就不那么重要了——但這意味着在第一種情況下,你還需要在調用代碼周圍使用#if DEBUG
。
我個人推薦第二種方法 - 但你確實需要在腦海中清楚地區分它們之間的區別。
我相信很多人會不同意我的觀點,但是作為一個構建人員,我經常聽到“但它可以在我的機器上運行!”,我認為你幾乎不應該使用它們。 如果你真的需要一些東西來測試和調試,想辦法使可測試性與實際生產代碼分開。
在單元測試中使用模擬抽象場景,為您要測試的一次性場景制作一次性版本,但不要將調試測試放入您測試和編寫用於生產版本的二進制代碼的代碼中。 這些調試測試只是向開發人員隱藏可能的錯誤,因此直到過程后期才會發現它們。
這個也很有用:
if (Debugger.IsAttached)
{
...
}
對於第一個示例,如果未定義DEBUG
,則構建中將不存在SetPrivateValue
;對於第二個示例,如果未定義DEBUG
,則構建中將不存在對SetPrivateValue
調用。
對於第一個示例,您還必須使用#if DEBUG
包裝對SetPrivateValue
任何調用。
在第二個示例中,將省略對SetPrivateValue
的調用,但請注意SetPrivateValue
本身仍將被編譯。 如果您正在構建庫,這很有用,因此引用您的庫的應用程序仍然可以使用您的函數(如果滿足條件)。
如果你想省略調用並節省被調用者的空間,你可以結合使用兩種技術:
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}
讓我們假設您的代碼還有一個#else
語句,它定義了一個空存根函數,解決了 Jon Skeet 的觀點之一。 兩者之間還有第二個重要區別。
假設#if DEBUG
或Conditional
函數存在於您的主項目可執行文件所引用的DLL 中。 使用#if
,將根據庫的編譯設置執行條件評估。 使用Conditional
屬性,將根據調用程序的編譯設置執行條件的評估。
我有一個 SOAP WebService 擴展來使用自定義[TraceExtension]
記錄網絡流量。 我僅將它用於調試版本並從發布版本中省略。 使用#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(...)
通常,您需要在 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
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.