C#泛型約束

風靈使發表於2018-09-12

六種型別的約束:

格式 描述
T:結構 型別引數必須是值型別。可以指定除 Nullable 以外的任何值型別。有關更多資訊,請參見使用可空型別(C# 程式設計指南)。
T:類 型別引數必須是引用型別,包括任何類、介面、委託或陣列型別。
T:new() 型別引數必須具有無引數的公共建構函式。當與其他約束一起使用時,new() 約束必須最後指定。
T:<基類名> 型別引數必須是指定的基類或派生自指定的基類。
T:<介面名稱> 型別引數必須是指定的介面或實現指定的介面。可以指定多個介面約束。約束介面也可以是泛型的。
T:U 為 T 提供的型別引數必須是為 U 提供的引數或派生自為 U 提供的引數。這稱為裸型別約束。

例子:

1.介面約束。

例如,可以宣告一個泛型類 MyGenericClass,這樣,型別引數 T 就可以實現 IComparable<T> 介面:

public class MyGenericClass<T> where T:IComparable { }

2.基類約束。

指出某個型別必須將指定的類作為基類(或者就是該類本身),才能用作該泛型型別的型別引數。這樣的約束一經使用,就必須出現在該型別引數的所有其他約束之前。

class MyClassy<T, U>
where T : class
where U : struct
{
}

3.建構函式約束。

以使用 new 運算子建立型別引數的例項;但型別引數為此必須受建構函式約束new() 的約束。new() 約束可以讓編譯器知道:提供的任何型別引數都必須具有可訪問的無引數(或預設)建構函式。new() 約束出現在 where 子句的最後。

public class MyGenericClass <T> where T: IComparable, new()
{
// The following line is not possible without new() constraint:
         T item = new T();
}

4.對於多個型別引數,每個型別引數都使用一個 where 子句。

interface MyI { }
class Dictionary<TKey,TVal>
where TKey: IComparable, IEnumerable
where TVal: MyI
{
public void Add(TKey key, TVal val)
{
}
}

5.還可以將約束附加到泛型方法的型別引數。

public bool MyMethod<T>(T t) where T : IMyInterface { }

6. 裸型別約束

用作約束的泛型型別引數稱為裸型別約束。當具有自己的型別引數的成員函式需要將該引數約束為包含型別的型別引數時,裸型別約束很有用。

class List<T>
{
void Add<U>(List<U> items) where U : T {/*...*/}
}

泛型類的裸型別約束的作用非常有限,因為編譯器除了假設某個裸型別約束派生自 System.Object 以外,不會做其他任何假設。在希望強制兩個型別引數之間的繼承關係的情況下,可對泛型類使用裸型別約束。

7.default關鍵字

之所以會用到default關鍵字,是因為需要在不知道型別引數為值型別還是引用型別的情況下,為物件例項賦初值。考慮以下程式碼:

class TestDefault<T>
    {
        public T foo()
        {
            T t = null; //???
            return t;
        }
    }

如果我們用int型來繫結泛型引數,那麼T就是int型,那麼註釋的那一行就變成了 int t = null;顯然這是無意義的。為了解決這一問題,引入了default關鍵字:

class TestDefault<T>
    {
        public T foo()
        {
                return default(T);
        }
    }

相關文章