本文大部分內容整理自msdn
3、分部方法
分部方法使類的某個部分的實施者能夠定義方法(類似於事件)。類的另一部分的實施者可以決定是否實現該方法。如果未實現該方法,編譯器將移除方法簽名以及對該方法的所有調用。因此,部分類別中的任何代碼都可以隨意地使用分部方法,即使未提供實現也是如此。如果調用了未實現的方法,將不會導致編譯時間錯誤或執行階段錯誤。
在自訂產生的程式碼時,分部方法特別有用。這些方法允許保留方法名稱和簽名,因此產生的程式碼可以調用方法,而開發人員可以決定是否實現方法。與部分類別非常類似,分部方法使代碼產生器建立的代碼和開發人員建立的代碼能夠協同工作,而不會產生運行時開銷。
分部方法聲明由兩個部分組成:定義和實現。它們可以位於部分類別的不同部分中,也可以位於同一部分中。
namespace PM
{
partial class A
{
partial void OnSomethingHappened(string s);
}
// This part can be in a separate file.
partial class A
{
// Comment out this method and the program
// will still compile.
partial void OnSomethingHappened(String s)
{
Console.WriteLine("Something happened: {0}", s);
}
}
}
要點:
1、分部方法聲明必須以內容關鍵字 partial 開頭,並且方法必須返回 void。
2、分部方法可以有 ref 參數,但不能有 out 參數。
3、分部方法為隱式 private 方法,因此不能為 virtual 方法。
4、分部方法不能為 extern 方法,因為主體的存在確定了方法是在定義還是在實現。
5、分部方法可以有 static 和 unsafe 修飾符。
6、分部方法可以為泛型的。約束將放在定義分部方法聲明上,但也可以選擇重複放在實現聲明上。參數和型別參數名稱在實現聲明和定義聲明中不必相同。
7、不能將委託轉換為分部方法。
情境舉例:輕量級事件處理
有的時候,自動產生的程式碼需要事件這類語言構造來通知使用者對某些操作進行處理,但實際上用於編寫的代碼就位於自動產生的類型之中。此時,或者需要觸發一個事件,或者就需要產生一個virtual方法來讓使用者繼承。但無論是事件還是繼承,開銷都是比較大的,所以可以通過分部方法來實現輕量級的處理方式。如下面的類:
partial class Customer
{
string name;
public string Name
{
get
{
return name;
}
set
{
OnBeforeUpdateName();
OnUpdateName();
name = value;
OnAfterUpdateName();
}
}
partial void OnBeforeUpdateName();
partial void OnAfterUpdateName();
partial void OnUpdateName();
}
這裡定義了三個分部方法,其意義不言而喻。假設這是系統自動產生的程式碼,則我們只需在另外一個原始碼檔案中的partial class Customer中實現這幾個分部方法即可。
另外在自訂DataContext中的Insert、Update、Delete方法也會用到部分類別,等我們講到DataContext時再詳細講解這部分。
4、擴充方法:
擴充方法使您能夠向現有類型“添加”方法,而無需建立新的衍生類別型、重新編譯或以其他方式修改原始類型。擴充方法是一種特殊的靜態方法,調用擴充方法與調用在類型中實際定義的方法之間沒有明顯的差異。擴充方法是可以通過使用執行個體方法文法調用的靜態方法。效果上,使得附加的方法擴充已存在類型和構造類型成為可能。他可以對現有類功能進行擴充,從而使該類型的執行個體具有更多的方法。擴充方法有助於把今天動態語言中流行的對快速錄入支援的靈活性,與強型別語言之效能和編譯時間驗證融合起來。這裡先舉一個msdn中的例子:
下面的樣本示範為 System.String 類定義的一個擴充方法。假設我要分析一個字串,希望得到字串中單詞的個數,一般情況下我們可能使用一個統計的函數來解決這個問題
static void Main(string[] args)
{
string s = "Hello Extension Methods";
Console.WriteLine(WordCount(s));
}
static int WordCount(string str)
{
return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
但這樣用起來感覺上可能會很彆扭,這個WordCount方法如果就是字串類中的一個方法多好,像所有字串的執行個體方法一樣用。這裡我們引出擴充方法來解決這個問題。
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
注意,上面的靜態方法在第一個類型是string的參數變數前有個“this”關鍵詞,這告訴編譯器,這個特定的擴充方法應該添加到類型為“string”的對象中去。
使用 using 指令將 WordCount 擴充方法放入範圍中:
using ExtensionMethods;
string s = "Hello Extension Methods";
int i = s.WordCount();
擴充方法要點
1、擴充方法的本質為將執行個體方法調用在編譯期改變為靜態類中的靜態方法調用。事實上,它確實擁有靜態方法所具有的所有功能。
2、通常,您更多時候是調用擴充方法而不是實現您自己的擴充方法。由於擴充方法是使用執行個體方法文法調用的,因此不需要任何特殊知識即可從用戶端代碼中使用它們。若要為特定類型啟用擴充方法,只需為在其中定義這些方法的命名空間添加 using 指令。
3、擴充方法的優先順序:現有執行個體方法優先順序最高,其次為最近的namespace下的靜態類的靜態方法,最後為較遠的namespace下的靜態類的靜態方法。(與介面或類方法具有相同名稱和簽名的擴充方法永遠不會被調用 )
4、在代碼中,可以使用執行個體方法文法調用該擴充方法。但是,編譯器產生的中繼語言 (IL) 會將代碼轉換為對靜態方法的調用。因此,並未真正違反封裝原則。實際上,擴充方法無法訪問它們所擴充的類型中的私人變數。
擴充方法通用準則
1、通常,建議您只在不得已的情況下才實現擴充方法,並謹慎地實現。只要有可能,必須擴充現有類型的用戶端代碼都應該通過建立從現有類型派生的新類型來達到這一目的。
2、在使用擴充方法來擴充您無法更改其原始碼的類型時,您需要承受該類型實現中的更改會導致擴充方法失效的風險。
3、如果您確實為給定類型實現了擴充方法,請記住以下兩點:
如果擴充方法與該類型中定義的方法具有相同的簽名,則擴充方法永遠不會被調用。
擴充方法被在命名空間層級放入範圍中。例如,如果您在同一個名為 Extensions 的命名空間中具有多個包含擴充方法的靜態類,則這些擴充方法將全部由 using Extensions; 指令放入範圍中。