擴充套件方法 - 概述
擴充套件方法在 C#3.0 中引入。擴充套件方法擴充套件並向現有型別新增行為,而不建立新的派生型別,重新編譯或以其他方式修改原始型別。*當你無法修改要增強的型別的源時,它們尤其有用。*可以為系統型別,由第三方定義的型別以及你自己定義的型別建立擴充套件方法。可以呼叫擴充套件方法,就好像它是原始型別的成員方法一樣。這允許方法鏈用於實現 Fluent 介面。
通過向靜態類新增靜態方法來建立擴充套件方法,該靜態類與要擴充套件的原始型別不同。持有擴充套件方法的靜態類通常是為了儲存擴充套件方法而建立的。
擴充套件方法採用特殊的第一個引數來指定要擴充套件的原始型別。第一個引數用關鍵字 this
(它構成了在 C#中的 this
的特殊和獨特用途 - 應理解為與使用 this
不同,後者允許引用當前物件例項的成員)。
在以下示例中,要擴充套件的原始型別是類 string
。String
已通過 Shorten()
方法進行了擴充套件,該方法提供了縮短的附加功能。已建立靜態類 StringExtensions
以儲存擴充套件方法。擴充套件方法 Shorten()
通過特別標記的第一個引數顯示它是 string
的擴充套件。為了表明 Shorten()
方法擴充套件 string
,第一個引數用 this
標記。因此,第一個引數的完整簽名是 this string text
,其中 string
是擴充套件的原始型別,text
是所選擇的引數名稱。
static class StringExtensions
{
public static string Shorten(this string text, int length)
{
return text.Substring(0, length);
}
}
class Program
{
static void Main()
{
// This calls method String.ToUpper()
var myString = "Hello World!".ToUpper();
// This calls the extension method StringExtensions.Shorten()
var newString = myString.Shorten(5);
// It is worth noting that the above call is purely syntactic sugar
// and the assignment below is functionally equivalent
var newString2 = StringExtensions.Shorten(myString, 5);
}
}
作為擴充套件方法的第一個引數傳遞的物件 (伴隨著 this
關鍵字)是呼叫擴充套件方法的例項。
例如,執行此程式碼時:
"some string".Shorten(5);
引數的值如下:
text: "some string"
length: 5
請注意,擴充套件方法僅在與其定義位於同一名稱空間中時才可用,如果使用擴充套件方法由程式碼顯式匯入名稱空間,或者擴充套件類是無名稱空間。 .NET 框架指南建議將擴充套件類放在它們自己的名稱空間中。但是,這可能會導致發現問題。
這導致擴充套件方法和正在使用的庫之間沒有衝突,除非明確地引入可能衝突的名稱空間。例如 LINQ Extensions :
using System.Linq; // Allows use of extension methods from the System.Linq namespace
class Program
{
static void Main()
{
var ints = new int[] {1, 2, 3, 4};
// Call Where() extension method from the System.Linq namespace
var even = ints.Where(x => x % 2 == 0);
}
}
從 C#6.0 開始,也可以將 using static
指令放到包含擴充套件方法的類中。例如,using static System.Linq.Enumerable;
。這使得來自該特定類的擴充套件方法可用,而無需將來自同一名稱空間的其他型別引入範圍。
當具有相同簽名的類方法可用時,編譯器將其優先於擴充套件方法呼叫。例如:
class Test
{
public void Hello()
{
Console.WriteLine("From Test");
}
}
static class TestExtensions
{
public static void Hello(this Test test)
{
Console.WriteLine("From extension method");
}
}
class Program
{
static void Main()
{
Test t = new Test();
t.Hello(); // Prints "From Test"
}
}
請注意,如果有兩個具有相同簽名的擴充套件函式,並且其中一個在同一名稱空間中,那麼將優先考慮該名稱。另一方面,如果 using
訪問了它們,那麼編譯時錯誤將隨之發生:
以下方法或屬性之間的呼叫不明確
請注意,通過 originalTypeInstance.ExtensionMethod()
呼叫擴充套件方法的語法方便是一個可選的便利。該方法也可以以傳統方式呼叫,以便將特殊的第一引數用作該方法的引數。
即,以下兩項工作:
//Calling as though method belongs to string--it seamlessly extends string
String s = "Hello World";
s.Shorten(5);
//Calling as a traditional static method with two parameters
StringExtensions.Shorten(s, 5);