在設計一個方法的參數時,可為部分或全部參數分配默認值。然後,調用這些方法的代碼時可以選擇不指定部分實參,接受默認值。此外,調用方法時,還可以通過指定參數名稱的方式為其傳遞實參。比如:
Int32 s_n = M(Int32 x=, String s = = (DateTime), Guid guid = M(, M( M(s_n++, s_n++ M(s: (s_n++).ToString(), x: s_n++}在定義的方法中,如果為部分參數指定了默認值,需注意下述原則: 1)可以為方法、構造器方法和有參屬性(C#索引器)的參數指定默認值。還可為屬於委托定義一部分的參數指定默認值。然後,在調用該委托類型的一個變量時,可以省略實參,以接受默認值。 2)有默認值的參數必須放在沒有默認值的所有參數之後。換言之,一旦定義了一個有默認值的參數,它右邊的所有參數也必須有默認值。但有個例外:"參數數組"這種參數必須放在所有參數(包括有默認值的這些)之後,而且數組本身不能有一個默認值。 3)默認值必須是編譯時能確定的常量值。這些參數的類型可以是C#認定的基元類型,還包括枚舉類型,以及設為null的任何引用類型。對於任何值類型的一個參數,可將默認值設為值類型的一個實例,並讓它的所有字段都包含零值。可以用default關鍵字或者new關鍵字來表達這個意思。如在M方法中設置dt參數和guid參數的默認值,就是用的這兩種語法。 4)注意不要重新命名(即修改)參數變量名稱。否則,任何調用者如果以傳參數名的方式傳遞實參,都必須修改它們的代碼。 5)如果方法是從模塊的外部調用的,更改參數的默認值具有潛在的危險性。調用方會在它的調用中嵌入默認值。如果以後更改參數的默認值,但沒有重新編譯調用方所在的代碼,它在調用你的方法時就會傳遞就得默認值。可考慮將默認值設為0/null作為哨兵值(起到占位子作用)使用。 6)如果參數使用ref或out關鍵字進行了標識,就不能設置默認值。因為沒有辦法為這些參數傳遞一個有意義的默認值。 使用可選或命名參數調用一個方法時,還要注意下述原則: 1)實參可按任何順序傳遞;但是,命名實參只能出現在實參列表的尾部。 2)可按名稱將實參傳給沒有默認值的參數。 3)C#不允許省略都好之間的實參,比如M(1, ,DateTime.Now)。 4)如果參數需要ref/out,為了以傳參數名的方式傳遞實參,請使用下面語法:
M( Int32 a =
在C#中,一旦為某個參數分配了一個默認值,編譯器就會在內部像該參數應用一個定制attibute,即System.Runtime.InteropServices.OptionalAttribute。這個attribute會在最終生成的文件的元數據中持久性地存儲下來。此外,編譯器還會向參數引用一個名為System.Runtime.InteropServices.DefaultParameterValueAttribute的attribute,並將這個attribute持久性存儲在最終文件的元數據中,然後,會向DefaultParameterValueAttribute的構造器中傳遞你在源代碼中指定的常量值。之後,一旦編譯器發現一個方法調用缺失了部分實參,就可以確定省略的是可選的實參,並從元數據中提取它們的默認值,將這些值自動嵌入調用中。 之後,一旦編譯器發現一個方法調用缺失了部分實參,就可以確定省略的是可選的實參,並從元數據中提取它們的默認值,並將這些值自動嵌入調用中。
針對一個方法中的隱式類型的局部變量,C#允許根據初始化表達式的類型來判斷它的類型。
name = x = (Exception); ShowVariableType(x); numbers = Int32[] { , , , collection = Dictionary<String, Single>() { { , ( item隱式類型的局部變量是局部變量,不能用它聲明方法的參數。也不能聲明一個類型的字段。 用var聲明的局部變量只是一種簡化語法,它要求編譯器根據一個表達式推斷具體的數據類型。var關鍵字只能用於聲明方法內部的局部變量,而dynamic關鍵字可用於局部變量,字段和參數。表達式不能轉型為var,但可以轉型為dynamic。必須實現初始化化var聲明的變量,但無需初始化用dynamic聲明的變量。 默認情況下,CLR假定所有的方法參數都是傳值的。 傳遞引用類型的對象時,對一個對象的引用(或者說指向對象的指針)會傳給方法。但這個引用(或指針)本身是以傳值方式傳給方法的。這意味著方法能修改對象,而調用者能看到這些修改。對於值類型的實例,傳給方法的是實例的一個副本,這意味著方法將獲取它專用的一個值類型實例副本,調用中的實例不受影響。 CLR中允許以傳引用而非傳值的方式傳遞參數。在C#中,這是用關鍵字out和ref。這兩個關鍵字都告訴C#編譯器生成的元數據來指明該參數時傳引用的。編譯器將生成代碼來傳遞參數的地址,而不是傳遞參數本身。 從CLR角度看,關鍵字out和ref完全一致。這就是說,無論用哪個關鍵字,都會生成相同的IL代碼。另外,元數據也幾乎一致。只有一個bit除外,它用於記錄聲明方法時指定的是out還是ref。 C#編譯器是將者兩個關鍵字區別對待的,而且這個區別決定了有哪個方法負責初始化所引用的對象。 如果方法的參數用out來標記,表明不指望調用者在調用方法之前初始化好了對象。被調用的方法不能讀取參數的值,而且在返回前必須向這個值寫入。相反,如果方法的參數用ref來標記,調用者就必須在調用方法前初始化參數的值,被調用的方法可以讀取值或者寫入值。 為值類型使用out和ref,效果等同於以傳值的方式傳遞引用類型。對於值類型,out和ref允許方法操縱單一的值類型實例。調用者必須為實例分配內存,被調用者則操縱該內存中的內容。 對於引用類型,調用代碼為一個指針分配內存(該指針指向一個引用類型的對象),被調用者則操縱這個指針。正因為如此,僅當方法"返回"對"方法知道的一個對象"的引用時,為引用類型提供out和ref才有意義。 四、向方法傳遞可變數量的參數 有的時候,開發人員想定義一個方法來獲取可變數量的參數。為了聲明方法接受可變數量的參數,如下:
Int32 Add(= (Int32 x = ; x < values.Length; x+++=我們調用時可以這樣:
Console.WriteLine(Add( Int32[] { , , , , }));
Console.WriteLine(Add(, , , , ));由於params關鍵字的存在,所以可以這麼做。params關鍵字告訴編譯器向參數引用System.ParamArrayAttribute的一個實例。 只有方法的最後一個參數才能用params關鍵字(ParamArrayAttribute)來標記。另外,這個參數只能標識任意類型的一個一位數組。可為這個參數傳遞null值,或傳遞對包含另個元素的一個數組的引用。
));那麼如果寫一個方法來獲取任意數量、任意類型的參數呢?只需要修改方法原型,讓它獲取一個Object[]而不是Int32[]。比如
DisplayTypes( (Object o
1)聲明方法的參數類型時,應盡量指定最弱的類型,最好是接口而不是基類。 例如,如果要寫一個方法處理一組數據項,最好是用接口(比如IEnumerable<T>)來聲明方法的參數,而不要使用強數據類型(比如List<T>)或者更強的接口類型(比如ICollection<T>或IList<T>):
MainpulateItems<T>(IEnumerable<T> MainpulateItems<T>(IEnumerable<T> ProcessBytes(Stream someStream) { ... }
2)一般最好將方法的返回類型聲明為最強的類型,以免受限於特定類型。例如:
Stream ProcessBytes() { ... }第一個方法是首選的,它允許方法的調用者選擇將返回對象視為一個FileStream對象或者一個Stream對象。但是,第二個方法要求調用者將返回對象視為一個Stream對象。總之,確保調用者在調用方法時有盡量大的靈活性,使方法的應用范圍更大。
六、常量性
CLR沒有提供對常量參數/對象的支持。