C#高級特性:擴展方法
- 擴展方法允許爲一個類定義新的方法但是不用操作這個類。擴展方法就是一個靜態類的靜態方法,這個方法的第一個參數的類型就是要擴展的這個類的類型(加this):
public static class StringHelper
{
public static bool IsCapitalized (this string s)
{
if (string.IsNullOrEmpty(s)) return false;
return char.IsUpper (s[0]);
}
}
IsCapitalized方法像實例方法一樣被string來調用,像這樣:
Console.WriteLine ("Perth".IsCapitalized());
擴展方法被編譯後會被編譯器轉換爲普通的靜態類的靜態方法的調用,像這樣:
Console.WriteLine (StringHelper.IsCapitalized ("Perth"));
這個轉換過程如下:
arg0.Method (arg1, arg2, ...); // 擴展方法的調用
StaticClass.Method (arg0, arg1, arg2, ...); // 編譯器將它轉換成靜態方法調用
接口也可以一樣的進行擴展:
public static T First<T> (this IEnumerable<T> sequence)
{
foreach (T element in sequence)
return element;
throw new InvalidOperationException ("No elements!");
}
...
Console.WriteLine ("Seattle".First()); // S
擴展方法在c#3.0引入。
擴展方法鏈
擴展方法像實例方法一樣,支持整潔的鏈式的函數調用,考慮下面的兩個函數:
public static class StringHelper
{
public static string Pluralize (this string s) {...}
public static string Capitalize (this string s) {...}
}
string x = "sausage".Pluralize().Capitalize();
string y = StringHelper.Capitalize (StringHelper.Pluralize ("sausage"));
x和y的結果是一樣的,只不過x是使用的實例方法的方式進行調用,y使用的是靜態方法進行調用。
多義性和解析
- 命名空間:除非在一個命名空間內(或利用using引入命名空間),否則我們不能訪問擴展方法,考慮下面的IsCapitalized方法:
using System;
namespace Utils
{
public static class StringHelper
{
public static bool IsCapitalized (this string s)
{
if (string.IsNullOrEmpty(s)) return false;
return char.IsUpper (s[0]);
}
}
}
如果要使用IsCapitalized,那麼下面的程序必須導入Utils命名空間,因爲這個方法在Utils命名空間中定義。否則會出現編譯錯誤。
namespace MyApp
{
using Utils;
class Test
{
static void Main()
{
Console.WriteLine ("Perth".IsCapitalized());
}
}
}
- 擴展方法和實例方法:任何兼容的實例方法總是優先於擴展方法調用,在下面的例子中,即使輸入一個參數類型爲int的參數,也會優先調用Test類中的Foo:
class Test
{
public void Foo (object x) { } // This method always wins
}
static class Extensions
{
public static void Foo (this Test t, int x) { }
}
- 擴展方法和擴展方法:如果兩個擴展方法名稱相同,那麼擴展方法必須作爲靜態方法來調用以加以區別,如果其中一個擴展方法的參數類型更具體,那麼這個方法被優先調用,例如下面這兩個類:
static class StringHelper
{
public static bool IsCapitalized (this string s) {...}
}
static class ObjectHelper
{
public static bool IsCapitalized (this object s) {...}
}
下面的代碼調用的是StringHelper的擴展方法:
bool test1 = "Perth".IsCapitalized();
如果要調用ObjectHelper的擴展方法,我們必須顯示的去調用:
bool test2 = (ObjectHelper.IsCapitalized ("Perth"));