使用#if/#endif 塊可以在同樣源碼上生成不同的編譯(結果),大多數debug 和release兩個版本。但它們決不是我們喜歡用的工具。由於#if/#endif很容易 被濫用,使得編寫的代碼難於理解且更難於調試。程序語言設計者有責任提供更 好的工具,用於生成在不同運行環境下的機器代碼。C#就提供了條件屬性 (Conditional attribute)來識別哪些方法可以根據環境設置來判斷是否應該被 調用。
(譯注:屬性在C#裡有兩個單詞,一個是property另一個是 attribute,它們有不是的意思,但譯為中文時一般都是譯為了屬性。property 是指一個對象的性質,也就是Item1裡說的屬性。而這裡的attribute指的是.Net 為特殊的類,方法或者property附加的屬性。可以在MSDN裡查找attribute取得 更多的幫助,總之要注意:attribute與property的意思是完全不一樣的。)
這個方法比條件編譯#if/#endif更加清晰明白。編譯器可以識別 Conditional屬性,所以當條件屬性被應用時,編譯器可以很出色的完成工作。 條件屬性是在方法上使用的,所以這就使用你必須把不同條件下使用的代碼要寫 到不同的方法裡去。當你要為不同的條件生成不同的代碼時,請使用條件屬性而 不是#if/#endif塊。
很多編程老手都在他們的項目裡用條件編譯來檢測 先決條件(per-conditions)和後續條件(post-conditions)。
(譯注: per-conditions,先決條件,是指必須滿足的條件,才能完成某項工作,而 post-conditions,後續條件,是指完成某項工作後一定會達到的條件。例如某 個函數,把某個對象進行轉化,它要求該對象不能為空,轉化後,該對象一定為 整形,那麼:per-conditions就是該對象不能為空,而post-conditions就是該 對象為整形。例子不好,但可以理解這兩個概念。)
你可能會寫一個私有 方法來檢測所有的類及持久對象。這個方法可能會是一個條件編譯塊,這樣可以 使它只在debug時有效。
private void CheckState( )
{
// The Old way:
#if DEBUG
Trace.WriteLine( "Entering CheckState for Person" );
// Grab the name of the calling routine:
string methodName =
new StackTrace( ).GetFrame( 1 ).GetMethod( ).Name;
Debug.Assert( _lastName != null,
methodName,
"Last Name cannot be null" );
Debug.Assert( _lastName.Length > 0,
methodName,
"Last Name cannot be blank" );
Debug.Assert( _firstName != null,
methodName,
"First Name cannot be null" );
Debug.Assert( _firstName.Length > 0,
methodName,
"First Name cannot be blank" );
Trace.WriteLine( "Exiting CheckState for Person" );
#endif
}
使用#if 和#endif編譯選項(pragmas),你已經為你的發布版(release)編譯出了一個空方 法。這個CheckState()方法會在所有的版本(debug和release)中調用。而在 release中它什麼也不做,但它要被調用。因此你還是得為例行公事的調用它而 付出小部份代價。
不管怎樣,上面的實踐是可以正確工作的,但會導致 一個只會出現在release中的細小BUG。下面的就是一個常見的錯誤,它會告訴你 用條件編譯時會發生什麼:
public void Func( )
{
string msg = null;
#if DEBUG
msg = GetDiagnostics( );
#endif
Console.WriteLine( msg );
}