.Net方法概述和分類
.Net方法概述
- 方法
“方法”是包含一系列語句的代碼塊。在 C# 中,每個執行指令都是在方法的上下文中完成的。方法在類或結構中聲明,聲明時,聲明時需要指定訪問級別、返回值、方法名稱以及任何方法參數。方法參數放在括號中,并用逗號隔開。空括號表示方法不需要參數。方法可以有或沒有參數,也可以有或沒有返回值。
- Main方法
Main 方法是控制臺應用程序或窗口應用程序的入口點。Main 方法是 .exe 程序的入口點,程序控制流在該處開始和結束。Main 在類或結構內聲明,Main 必須是靜態的,且不應該是公用的,但不要求封閉類或結構是靜態的。Main 的返回類型有兩種:void 或 int。所聲明的 Main 方法可以具有包含命令行實參的 string[] 形參,也可以不具有這樣的形參。使用 Visual Studio 創建 Windows 窗體應用程序時,可以手動添加形參,也可以使用 Environment 類獲取命令行實參。 形參讀取為從零開始編制索引的命令行實參。與 C 和 C++ 不同,在 C# 中不將程序名稱視為第一個命令行實參。
- 重載
定義一組名字相同的成員,但他們的參數數量或類型不同。
- 參數
對于被調用的方法,傳入的變量稱為“參數”。方法所接收的參數也是在一組括號中提供的,但必須指定每個參數的類型和名稱。該名稱不必與參數相同。
- 參數修飾符
- 無:值傳遞,原始數據的一份副本
- out:引用傳遞,方法未給該參數賦值會出現編譯錯誤
- ref:引用傳遞,方法未給該參數賦值也不會出現編譯錯誤
- params:允許將一組可變數量的參數作為單獨的邏輯參數進行傳遞,方法中只能有一個params,必須是方法中的最后一個參數
- 參數傳遞
- 值傳遞(默認)
- 值類型:傳遞的是數據值的副本
- 引用傳遞
- out
- ref
- 值傳遞(默認)
- 可選參數
指定參數的默認值,默認值必須在編譯時確定而不能在運行時確定,只能放在方法參數的最后。
- 命名參數調用方法
使用命名參數可以在方法調用時更換參數的順序。
- 返回值
方法可以向調用方返回值。如果返回類型(方法名稱前列出的類型)不是 void,則方法可以使用 return 關鍵字來返回值。
- 返回值修飾符
- 無返回值:void
- 有返回值:返回值的類型名
- 方法簽名
- 訪問修飾符
- private:私有的,類本身才能訪問
- protected:受保護的,類本身及派生類可訪問
- internal:內部的,同一程序集內部類型才能訪問
- public:公共的,內部與外部都可以訪問
- 可選修飾符
- irtual:虛擬的
- static:靜態的
- abstract:抽象的
- sealed:封閉的
- override:繼承的
- 訪問修飾符
#p#
.NET 泛型方法
- 泛型方法
泛型方法是使用類型參數聲明的方法。
- static void Swap<T>(ref T lhs, ref T rhs)
- {
- T temp;
- temp = lhs;
- lhs = rhs;
- rhs = temp;
- }
- 類型推斷
相同的類型推斷規則也適用于靜態方法以及實例方法。編譯器能夠根據傳入的方法參數推斷類型參數;它無法僅從約束或返回值推斷類型參數。因此,類型推斷不適用于沒有參數的方法。類型推斷在編譯時、編譯器嘗試解析任何重載方法簽名之前進行。編譯器向共享相同名稱的所有泛型方法應用類型推斷邏輯。在重載解析步驟中,編譯器僅包括類型推斷取得成功的那些泛型方法。
- Swap(ref a, ref b);
- 非泛型方法使用泛型參數
在泛型類中,非泛型方法可以訪問類級別類型參數。
- class SampleClass<T>
- {
- void Swap(ref T lhs, ref T rhs) { }
- }
- 泛型類與泛型方法使用相同的泛型參數
如果定義的泛型方法接受與包含類相同的類型參數,編譯器將生成警告 CS0693,因為在方法范圍內,為內部 T 提供的參數將隱藏為外部 T 提供的參數。除了類初始化時提供的類型參數之外,如果需要靈活調用具有類型參數的泛型類方法,請考慮為方法的類型參數提供其他標識符,如下面示例中的 GenericList2<T> 所示。
- class GenericList<T>
- {
- // CS0693
- void SampleMethod<T>() { }
- }
- class GenericList2<T>
- {
- //No warning
- void SampleMethod<U>() { }
- }
- 泛型約束
使用約束對方法中的類型參數啟用更專門的操作。此版本的 Swap<T> 現在稱為 SwapIfGreater<T>,它只能與實現 IComparable<T> 的類型參數一起使用。
- void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
- {
- T temp;
- if (lhs.CompareTo(rhs) > 0)
- {
- temp = lhs;
- lhs = rhs;
- rhs = temp;
- }
- }
- 方法重載
泛型方法可以使用許多類型參數進行重載。
- void DoWork() { }
- void DoWork<T>() { }
- void DoWork<T, U>() { }
#p#
.Net擴展方法
- 擴展方法
擴展方法使您能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。 對于用 C# 和 Visual Basic 編寫的客戶端代碼,調用擴展方法與調用在類型中實際定義的方法之間沒有明顯的差異。
通常,建議您只在不得已的情況下才實現擴展方法,并謹慎地實現。 只要有可能,必須擴展現有類型的客戶端代碼都應該通過創建從現有類型派生的新類型來達到這一目的。
- 限制條件
- 必須定義在靜態類中
- 必須使用this關鍵字對第一個參數進行修飾
- 每個擴展方法只能被內存中正確的實例和其所在的靜態類調用
- 代碼示例
- namespace DefineIMyInterface
- {
- using System;
- public interface IMyInterface
- {
- void MethodB();
- }
- }
- namespace Extensions
- {
- using System;
- using DefineIMyInterface;
- public static class Extension
- {
- public static void MethodA(this IMyInterface myInterface, int i)
- {
- Console.WriteLine
- ("Extension.MethodA(this IMyInterface myInterface, int i)");
- }
- public static void MethodA(this IMyInterface myInterface, string s)
- {
- Console.WriteLine
- ("Extension.MethodA(this IMyInterface myInterface, string s)");
- }
- public static void MethodB(this IMyInterface myInterface)
- {
- Console.WriteLine
- ("Extension.MethodB(this IMyInterface myInterface)");
- }
- }
- }
- namespace ExtensionMethodsDemo1
- {
- using System;
- using Extensions;
- using DefineIMyInterface;
- class A : IMyInterface
- {
- public void MethodB() { Console.WriteLine("A.MethodB()"); }
- }
- class B : IMyInterface
- {
- public void MethodB() { Console.WriteLine("B.MethodB()"); }
- public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
- }
- class C : IMyInterface
- {
- public void MethodB() { Console.WriteLine("C.MethodB()"); }
- public void MethodA(object obj)
- {
- Console.WriteLine("C.MethodA(object obj)");
- }
- }
- class ExtMethodDemo
- {
- static void Main(string[] args)
- {
- A a = new A();
- B b = new B();
- C c = new C();
- a.MethodA(1); // Extension.MethodA(object, int)
- a.MethodA("hello"); // Extension.MethodA(object, string)
- a.MethodB(); // A.MethodB()
- b.MethodA(1); // B.MethodA(int)
- b.MethodB(); // B.MethodB()
- b.MethodA("hello"); // Extension.MethodA(object, string)
- c.MethodA(1); // C.MethodA(object)
- c.MethodA("hello"); // C.MethodA(object)
- c.MethodB(); // C.MethodB()
- }
- }
- }
#p#
.NET 分部方法
- 分部方法
分部類或結構可以包含分部方法。 類的一個部分包含方法的簽名。 可以在同一部分或另一個部分中定義可選實現。 如果未提供該實現,則會在編譯時移除方法以及對方法的所有調用。
分部方法使類的某個部分的實施者能夠定義方法(類似于事件)。 類的另一部分的實施者可以決定是否實現該方法。 如果未實現該方法,編譯器將移除方法簽名以及對該方法的所有調用。 調用該方法,包括調用中的任何計算結果,在運行時沒有任何影響。 因此,分部類中的任何代碼都可以隨意地使用分部方法,即使未提供實現也是如此。 如果調用了未實現的方法,將不會導致編譯時錯誤或運行時錯誤。
在自定義生成的代碼時,分部方法特別有用。 這些方法允許保留方法名稱和簽名,因此生成的代碼可以調用方法,而開發人員可以決定是否實現方法。 與分部類非常類似,分部方法使代碼生成器創建的代碼和開發人員創建的代碼能夠協同工作,而不會產生運行時開銷。
分部方法聲明由兩個部分組成:定義和實現。 它們可以位于分部類的不同部分中,也可以位于同一部分中。 如果不存在實現聲明,則編譯器將優化定義聲明和對方法的所有調用。
- 限制條件
- 分部方法聲明必須以上下文關鍵字 partial 開頭,并且方法必須返回 void。
- 分部方法可以有 ref 參數,但不能有 out 參數。
- 分部方法為隱式 private 方法,因此不能為 virtual 方法。
- 分部方法不能為 extern 方法,因為主體的存在確定了方法是在定義還是在實現。
- 分部方法可以有 static 和 unsafe 修飾符。
- 分部方法可以為泛型的。約束將放在定義分部方法聲明上,但也可以選擇重復放在實現聲明上。參數和類型參數名稱在實現聲明和定義聲明中不必相同。
- 您可以為已定義并實現的分部方法生成委托,但不能為已經定義但未實現的分部方法生成委托。
- 代碼示例
- partial void onNameChanged();
- partial void onNameChanged(){}