2010-09-24 76 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) 
{ 
... 
}