《.net編程先鋒C#》第九章 配置和調度(轉)

來源:互聯網
上載者:User
編程 第九章 配置和調度
在上一章,你學到如何建立一個通用語言運行時(CLR)組件,且如何在一個簡單的測試應用程式中使用它。雖然CLR組件就要準備裝載了,但你還是應該思考以下技術之一:
。條件編譯
。文檔注釋
。代碼版本化

9.1 條件編譯
沒有代碼的條件編譯功能,我就不能繼續工作。條件編譯允許執行或包括基於某些條件的代碼;例如,產生應用程式的一個查錯(DEBUG)版本、示範(DEMO)版本或零售(RELEASE)版本。可能被包括或被執行的代碼的例子為許可證代碼、 螢幕保護裝置或你出示的任何程式。
在C#中,有兩種進行條件編譯的方法:
。預先處理用法
。條件屬性
9.1.1 預先處理用法
在C++中,在編譯器開始編譯代碼之前,預先處理步驟是分開的。在C#中,預先處理被編譯器自己類比—— 沒有分離的預先處理。它只不過是條件編譯。
儘管C#編譯器不支援宏,但它具有必需的功能,依據符號定義的條件,排除和包括代碼。以下小節介紹了在C#中受支援的各種標誌,它們與在C++中看到的相似。
。定義符號
。依據符號排除代碼
。引起錯誤和警告
9.1.1.1 定義符號
你不能使用隨C#編譯器一起的預先處理建立“define 標誌:符號:定義 ”宏,但是,你仍可以定義符號。根據某些符號是否被定義,可以排除或包括代碼。
第一種定義符號的辦法是在C#源檔案中使用 #define標誌:
#define DEBUG
這樣定義了符號DEBUG,且範圍在它所定義的檔案內。請注意,必須要先定義符號才能使用其它語句。例如,以下程式碼片段是不正確的:

using System;
#define DEBUG

編譯器將標記上述代碼為錯誤。你也可以使用編譯器定義符號(用於所有的檔案):
csc /define:DEBUG mysymbols.cs
如果你想用編譯器定義多種符號,只需用分號隔開它們:
csc /define:RELEASE;DEMOVERSION mysymbols.cs
在C#源檔案中,對這兩種符號的定義分為兩行 #define 標誌。
有時,你可能想要取消源檔案中(例如,較大項目的源檔案)的某種符號。可以用 #undef 標誌取消定義:
#undef DEBUG
#define的“定義標誌:符號: 定義”規則同樣適用於#undef: 它的範圍在自己定義的檔案之內,要放在任何語句如using語句之前。
這就是全部有關用C#預先處理定義符號和取消定義符號所要瞭解的知識。以下小節說明如何使用符號有條件地編譯代碼。

9.1.1.2 依據符號包括和排除代碼
最重要的“if標誌:符號:包括代碼”方式的目的為,依據符號是否被定義,有條件地包括和排除代碼。清單9.1 包含了已出現過的源碼,但這次它依據符號被有條件地編譯。

清單 9.1 利用 #if 標誌有條件地包括代碼

1: using System;
2:
3: public class SquareSample
4: {
5: public void CalcSquare(int nSideLength, out int nSquared)
6: {
7: nSquared = nSideLength * nSideLength;
8: }
9:
10: public int CalcSquare(int nSideLength)
11: {
12: return nSideLength*nSideLength;
13: }
14: }
15:
16: class SquareApp
17: {
18: public static void Main()
19: {
20: SquareSample sq = new SquareSample();
21:
22: int nSquared = 0;
23:
24: #if CALC_W_OUT_PARAM
25: sq.CalcSquare(20, out nSquared);
26: #else
27: nSquared = sq.CalcSquare(15);
28: #endif
29: Console.WriteLine(nSquared.ToString());
30: }
31: }

注意,在這個源檔案中沒有定義符號。當編譯應用程式時,定義(或取消定義)符號:
csc /define:CALC_W_OUT_PARAM square.cs
根據“ if標誌:符號:包括代碼”的符號定義,不同的 CalcSquare 被調用了。用來對符號求值的類比預先處理標誌為#if、 #else和 #endif。它們產生的效果就象C#相應的if 語句那樣。你也可以使用邏輯“與”(&&)、邏輯“或”(¦¦)以及“否”(!)。它們的例子顯示在清單9.2 中。

清單 9.2 使用#elif 在#if標誌中建立多個分支

1: // #define DEBUG
2: #define RELEASE
3: #define DEMOVERSION
4:
5: #if DEBUG
6: #undef DEMOVERSION
7: #endif
8:
9: using System;
10:
11: class Demo
12: {
13: public static void Main()
14: {
15: #if DEBUG
16: Console.WriteLine("Debug version");
17: #elif RELEASE && !DEMOVERSION
18: Console.WriteLine("Full release version");
19: #else
20: Console.WriteLine("Demo version");
21: #endif
22: }
23: }

在這個“if標誌:符號:包含代碼”例子中,所有的符號都在C#源檔案中被定義。注意第6行#undef語句增加的那部分。由於不編譯DEBUG代碼的DEMO版本(任意選擇),我確信它不會被某些人無意中定義了,而且總當DEBUG被定義時,就取消DEMO版本的定義。
接著在第15~21行,預先處理符號被用來包括各種代碼。注意#elif標誌的用法,它允許你把多個分支加到#if 標誌。該代碼運用邏輯操作符“&&”和非操作符“!”。也可能用到邏輯操作符“¦¦”,以及等於和不等於操作符。

9.1.1.3 引起錯誤並警告
另一種可能的“警告 標誌錯誤 標誌”預先處理標誌的使用,是依據某些符號(或根本不依據,如果你這樣決定)引起錯誤或警告。各自的標誌分別為 #warning和#error,而清單9.3 示範了如何在你的代碼中使用它們。
清單 9.3 使用預先處理標誌建立編譯警告和錯誤

1: #define DEBUG
2: #define RELEASE
3: #define DEMOVERSION
4:
5: #if DEMOVERSION && !DEBUG
6: #warning You are building a demo version
7: #endif
8:
9: #if DEBUG && DEMOVERSION
10: #error You cannot build a debug demo version
11: #endif
12:
13: using System;
14:
15: class Demo
16: {
17: public static void Main()
18: {
19: Console.WriteLine("Demo application");
20: }
21: }

在這個例子中,當你產生一個不是DEBUG版本的DEMO版本時,就發出了一個編譯警告(第5行~第7行)。當你企圖產生一個DEBUG DEMO版本時,就引起了一個錯誤,它阻止了可執行檔的產生。對比起前面只是取消定義令人討厭的符號的例子,這些代碼告訴你,“警告 標誌錯誤 標誌”企圖要做的工作被認為是錯誤的。這肯定是更好的處理辦法。
9.1.1.4 條件屬性
C++的預先處理也許最經常被用來定義宏,宏可以解決一種程式產生時的函數調用,而卻不能解決另一種程式產生時的任何問題。這些例子包括 ASSERT和TRACE 宏,當定義了DEBUG符號時,它們對函數調用求值,當產生一個RELEASE版本時,求值沒有任何結果。

當瞭解到宏不被支援時,你也許會猜測,條件功能已經消亡了。幸虧我可以報道,不存在這種情況。你可以利用條件屬性,依據某些已定義符號來包括方法。:

[conditional("DEBUG")]
public void SomeMethod() { }

僅當符號DEBUG被定義時,這個方法被加到可執行檔。並且調用它,就象
SomeMethod();

當該方法不被包括時,它也被編譯器聲明。功能基本上和使用C++條件宏相同。
在例子開始之前,我想指出,條件方法必須具有void的傳回型別,不允許其它傳回型別。然而,你可以傳遞你想使用的任何參數。
在清單9.4 中的例子示範了如何使用條件屬性重建具有C++的TRACE宏一樣的功能。為簡單起見,結果直接輸出到螢幕。你也可以根據需要把它定向到任何地方,包括一個檔案。

清單 9.4 使用條件屬性實現方法

1: #define DEBUG
2:
3: using System;
4:
5: class Info
6: {
7: [conditional("DEBUG")]
8: public static void Trace(string strMessage)
9: {
10: Console.WriteLine(strMessage);
11: }
12:
13: [conditional("DEBUG")]
14: public static void TraceX(string strFormat,params object[] list)
15: {
16: Console.WriteLine(strFormat, list);
17: }
18: }
19:
20: class TestConditional
21: {
22: public static void Main()
23: {
24: Info.Trace("Cool!");
25: Info.TraceX("{0} {1} {2}","C", "U", 2001);
26: }
27: }

在Info類中,有兩個靜態方法,它們根據DEBUG符號被有條件地編譯:Trace,接收一個參數,而TraceX則接收n個參數。Trace的實現直接了當。然而,TraceX實現了一個你從沒有見過的關鍵字:params。
params 關鍵字允許你指定一個方法參數,它實際上接收了任意數目的參數。其類似C/C++的省略參數。注意,它必須是方法調用的最後一個參數,而且在參數列表中,你只能使用它一次。畢竟,它們的局限性極其明顯。
使用params 關鍵字的意圖就是要擁有一個Trace方法,該方法接收一個格式字串以及無數個置換對象。幸好,還有一個支援格式字串和對象數組的 WriteLine方法(第16行)。
這個小程式產生的哪一個輸出完全取決於DEBUG是否被定義。當DEBUG符號被定義時,方法都被編譯和執行。如果DEBUG不被定義,對Trace和TraceX的調用也隨之消失。
條件方法是給應用程式和組件增加條件功能的一個真正強大的手段。用一些技巧,你就可以根據由邏輯“或”(¦¦)以及邏輯“與”(&&)串連起來的多個符號,產生條件方法。然而,對於這些方案,我想給你推薦C#文檔。

9.2 在XML中的文檔注釋
很多程式員根本不喜歡的一項任務就是寫作,包括寫注釋和寫文檔。然而,有了C#,你就找到改變老習慣的好理由:你可以用代碼的注釋自動產生文檔。
由編譯器產生的輸出結果是完美的XML。它可以作為組件文檔的輸入被使用,以及作為顯示協助並揭示組件內部細節的工具。例如, Visual Studio 7 就是這樣一種工具。
這一節專門為你說明如何最好地運用C#的文檔功能。該例子涉及的範圍很廣,所以你不能有這樣的借口,說它過於複雜,以至很難領會如何加入文檔注釋。文檔是軟體極其重要的一部分,特別是要被其他開發人員使用的組件的文檔。
在以下小節中,文檔註解用來說明RequestWebPage 類。我已分別在以下幾小節中做出解釋:
。描述一個成員
。添加備忘和列表
。提供例子
。描述參數
。描述屬性
。編譯文檔


9.2.1 描述一個成員
第一步,為一個成員添加一個簡單的描述。你可以用  標籤這樣做:
/// This is ....


每一個文檔注釋起始於由三個反斜線組成的符號“///”。你可以把文檔注釋放在想要描述的成員之前:

/// Class to tear a Webpage from a Webserver

public class RequestWebPage

使用和 標籤,為描述添加段落。用標籤引用其它已有了注釋的成員。
/// Included in the  class

增加一個連結到RequestWebPage類的描述。注意,用於標籤的文法是XML文法,這意味著標籤大寫化的問題,而且標籤必須正確地嵌套。
當為一個成員添加文檔時,另一個有趣的標籤是 。它允許你描述可能使讀者非常感興趣的其它話題。

///

前面的例子告訴讀者,他可能也想查閱System.Net 名字空間的文檔。你一定要給超出當前範圍的項目規定一個完全資格名。
作為確保,清單9.5 包含 RequestWebPage類中正在工作的文檔的所有例子。看一下如何使用標籤以及嵌套如何為組件產生文檔。

清單 9.5 利用 , , , and  標籤描述一個成員

1: using System;
2: using System.Net;
3: using System.IO;
4: using System.Text;
5:
6: /// Class to tear a Webpage from a Webserver
7: public class RequestWebPage
8: {
9: private const int BUFFER_SIZE = 128;
10:
11: /// m_strURL stores the URL of the Webpage
12: private string m_strURL;
13:
14: /// RequestWebPage() is the constructor for the class
15: ///  when called without arguments.
16: public RequestWebPage()
17: {
18: }
19:
20: /// RequestWebPage(string strURL) is the constructor for the class
21: ///  when called with an URL as parameter.
22: public RequestWebPage(string strURL)
23: {
24: m_strURL = strURL;
25: }
26:
27: public string URL
28: {
29: get { return m_strURL; }
30: set { m_strURL = value; }
31: }
32:
33: /// The GetContent(out string strContent) method:
34: /// Included in the  class
35: /// Uses variable
36: /// Used to retrieve the content of a Webpage. The URL
37: /// of the Webpage (includinghttp://) must already be
38: /// stored in the private variable m_strURL.
39: /// To do so, call the constructor of the RequestWebPage
40: /// class, or set its property  to the URL string.
41: ///
42: ///
43: ///
44: ///
45: ///
46: ///  
47: ///
48: ///
49:
50: public bool GetContent(out string strContent)
51: {
52: strContent = "";
53: // ...
54: return true;
55: }
56: }

9.2.2 添加備忘和列表
標籤是規定大量文檔的地方。與之相比, 只僅僅規定了成員的簡短描述。
你不限於只提供段落文本(使用標籤)。例如,你可以在備忘部分包含bulleted(和有限偶數)列表(list):

///
/// Constructor
///  or
///
///
///

這個list有一項(item),且該item引用了兩個不同的建構函式描述。你可以根據需要,任意往list item中新增內容。
另一個在備忘部分很好用的標籤是。例如,你可以用來引用和描述傳遞給建構函式的參數:

/// Stores the URL from the parameter ///  in
/// the private variable .
public RequestWebPage(string strURL)

在清單9.6中,你可以看到所有的這些以及前面的標籤正在起作用。

清單9.6 為文檔添加一個備忘和bullet list

1: using System;
2: using System.Net;
3: using System.IO;
4: using System.Text;
5:
6: /// Class to tear a Webpage from a Webserver
7: /// The class RequestWebPage provides:
8: /// Methods:
9: ///
10: /// Constructor
11: ///  or
12: ///
13: ///
14: ///
15: ///
16: /// Properties:
17: ///
18: ///
19: ///
20: ///
21: ///
22: ///
23: ///
24: public class RequestWebPage
25: {
26: private const int BUFFER_SIZE = 128;
27:
28: /// m_strURL stores the URL of the Webpage
29: private string m_strURL;
30:
31: /// RequestWebPage() is the constructor for the class
32: ///  when called without arguments.
33: public RequestWebPage()
34: {
35: }
36:
37: /// RequestWebPage(string strURL) is the constructor for the class
38: ///  when called with an URL as parameter.
39: /// Stores the URL from the parameter  in
40: /// the private variable .
41: public RequestWebPage(string strURL)
42: {
43: m_strURL = strURL;
44: }
45:
46: /// Sets the value of .
47: /// Returns the value of .
48: public string URL
49: {
50: get { return m_strURL; }
51: set { m_strURL = value; }
52: }
53:
54: /// The GetContent(out string strContent) method:
55: /// Included in the  class
56: /// Uses variable
57: /// Used to retrieve the content of a Webpage. The URL
58: /// of the Webpage (includinghttp://) must already be
59: /// stored in the private variable m_strURL.
60: /// To do so, call the constructor of the RequestWebPage
61: /// class, or set its property  to the URL string.
62: ///
63: /// Retrieves the content of the Webpage specified in
64: /// the property and hands it over to the out
65: /// parameter .
66: /// The method is implemented using:
67: ///
68: /// The method.
69: /// The  method.
70: /// The method
71: /// The  method
72: /// The  method
73: /// The  property together with its
74: ///  method
75: /// The  method for the
76: ///  object.
77: ///
78: ///
79: ///
80: public bool GetContent(out string strContent)
81: {
82: strContent = "";
83: // ...
84: return true;
85: }
86: }

9.2.3 提供例子
要想說明一個對象和方法的用法,最好的辦法是提供優秀原始碼的例子。因此,不要詫異文檔注釋也有用於聲明例子的標籤:  and 。 標籤包含了包括描述和代碼的整個例子,而  標籤僅包含了例子的代碼(令人驚訝)。
清單9.7 說明如何?代碼例子。包括的例子用於兩個建構函式。你必須給GetContent方法提供例子。

清單.7 利用例子解釋概念

1: using System;
2: using System.Net;
3: using System.IO;
4: using System.Text;
5:
6: /// Class to tear a Webpage from a Webserver
7: ///  ...
8: public class RequestWebPage
9: {
10: private const int BUFFER_SIZE = 12


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.