C#函數式程序設計之約束類型 每當使用泛型類型時,可以通過where字句對泛型添加約束: static void OutputValue<T>(T value) where T : ListItem<string> { Console.WriteLine("String list value: {0}", value.Value); } 這個例子直觀地聲明了一個約束:類型T必須與ListItem<string>相匹配。泛型類型約束T:X表示T可以是X、X的派生對象或X的實現(假如X是一個接口)。換言之,假如類型T的一個實例為t,則可以把它賦給一個變量:X x=t; 約束可以使用具體的類型,但是在這些情形下,類型不可以是密封的。有幾個特殊的關鍵字可以取代或補充類型聲明符。關鍵字class表示此類型必須是一個引用類型,而struct表示它必須是一個值類型。當new()與class或者任何具體類型一起使用時,可以給這個類型定義一個默認的構造函數。 約束的最後一個應用是定義兩個類型參數的關系。例如,對於類型參數的T和U,約束T:U表示T必須與U相容。 使用約束時,有一點必須記住:泛型的基本作用是提供一個類型安全的方法,使代碼可以處理不同類型的數據。約束用得越多,則離這個思想越遠,因為約束降低了靈活性。 C#函數式程序設計之其他泛型類型 除了方法與類外,結構體、委托和接口也可以使用類型參數。結構體和接口使用類型參數是顯而易見的,其用法與類相似: public struct MyPoint<T> where T : struct { public MyPoint(T x, T y) { this.x = x; this.y = y; } private readonly T x; public T X { get { return x; } } private readonly T y; public T Y { get { return y; } } public interface IListItem<T> { T Value { get; } ListItem<T> Prepend(T value); } } 即使是委托,其用法也絲毫沒有令人吃驚的地方: public delegate R CreateDelegate<T, R>(T param); public class ParameterFactory<T, R> { CreateDelegate<T, R> createDelegate; public ParameterFactory(CreateDelegate<T, R> createDelegate) { this.createDelegate = createDelegate; } } 使用了泛型後,這些委托幾乎可以代表任何函數。 C#函數式程序設計之協變與逆變 如果一個操作保留了類型原來的順序,則成為協變,如果顛倒它們的順序,則稱為逆變。所謂的類型順序是指:通用類型的順序值比專用類型的順序值強。 下面這個例子說明C#支持協變,首先定義一個對象數組: object[] objects = new object[3]; objects[0] = new object(); objects[1]="Just a string"; objects[2]=10; 可以把不同的值插入到這個數組中,因為所有數據最終都是派生自.NET中的Object類型。換言之,Object是一個非常通用的類型,即它是一個強類型。接下來說明.NET支持協變,它把一個弱類型的值賦給強類型的變量: string[] stringsTest = new string[] { "one", "two", "three" }; objects = stringsTest; 變量objects屬於object[]類型,它可以保存實際類型為string[]的值。仔細想想,我們希望如此,但是結果不是這樣的,畢竟,雖然string派生自object,但是string[]並不是派生自object[]。盡管如此,由於本例中C#支持協變,這個賦值是可行的。 說明逆變思想需要一個比較復雜的例子: public class Person:IPerson { public Person() { } } public class Woman : Person { public Woman() { } } Woman是從Person派生出來的類,現在分析如下兩個函數: static void WorkWithPerson(Person person) { } static void WorkWithWonman(Woman woman) { } 其中一個函數作用於Woman類,另一個函數比較通用,作用於Person類。從Woman類可以定義以下兩個委托和函數: delegate void AcceptWomanDelegate(Woman person); static void DoWork(Woman woman, AcceptWomanDelegate acceptWoman) { acceptWoman(woman); } DoWork函數接受一個Woman參數和一個函數引用,後者也接受一個Woman參數。DoWork函數把Woman實例傳遞給委托。元素類型大小為:Person比Woman強,WorkWithPerson比WorkWithWoman強,為了應用逆變,在此認為WorkWithPerson比AcceptWomanDelegate強,看以下三行代碼: Woman woman = new Woman(); DoWork(woman, WorkWithWonman); DoWork(woman, WorkWithPerson); 首先創建一個Woman實例,然後調用DoWork函數,把Woman實例和WorkWithWoman方法的引用地址傳遞給DoWork。後者顯然是與委托類型AcceptWomanDelegate相容——兩者都只有一個Woman類型參數,沒有返回值。但第三行代碼有點怪,根據AcceptWomanDelegate的要求,WorkWithPerson方法接受一個Person參數,而不是一個Woman參數。雖然如此,WorkWithPerson還是與委托類型相容,這是逆變的緣故。 因此,在委托類型下,強類型可以保存在弱類型的變量中。 變異也能應用在泛型中。如下代碼: List<object> objectList = new List<object>(); List<string> stringList = new List<string>(); objectList = stringList;