类型约束

在定义泛型类时,能够对客户端代码可以在实例化类时用于类型参数的类型种类施加限制。若是客户端代码尝试使用某个约束所不容许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。下表列出了六种类型的约束:编程

T:结构
类型参数必须是值类型。能够指定除 Nullable 之外的任何值类型。有关更多信息,请参见使用可空类型(C# 编程指南)。数组

T:类
类型参数必须是引用类型,包括任何类、接口、委托或数组类型。函数

T:new()
类型参数必须具备无参数的公共构造函数。当与其余约束一块儿使用时,new() 约束必须最后指定。测试

T: <基类名>
类型参数必须是指定的基类或派生自指定的基类。 ui

T: <接口名称>
类型参数必须是指定的接口或实现指定的接口。能够指定多个接口约束。约束接口也能够是泛型的。 设计

T:U
为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。code

使用约束的缘由对象

若是要检查泛型列表中的某个项以肯定它是否有效,或者将它与其余某个项进行比较,则编译器必须在必定程度上保证它须要调用的运算符或方法将受到客户端代码可能指定的任何类型参数的支持。这种保证是经过对泛型类定义应用一个或多个约束得到的。例如,基类约束告诉编译器:仅此类型的对象或今后类型派生的对象才可用做类型参数。一旦编译器有了这个保证,它就可以容许在泛型类中调用该类型的方法。约束是使用上下文关键字 where 应用的。下面的代码示例演示可经过应用基类约束添加到 GenericList 类(在泛型介绍(C# 编程指南)中)的功能。 继承

public class Employee  
{  
    private string name;  
    private int id;    

    public Employee(string s, int i)  
    {  
        name = s;  
        id = i;  
    }  

    public string Name  
    {  
        get { return name; }  
        set { name = value; }  
    }   
    public int ID  
    {  
        get { return id; }  
        set { id = value; }  
    }  
}  

public class GenericList<T> where T : Employee  
{  
    private class Node  
    {  
        private Node next;  
        private T data;  


        public Node(T t)  
        {  
            next = null;  
            data = t;  
        }  

        public Node Next  
        {  
            get { return next; }  
            set { next = value; }  
        }  

        public T Data  
        {  
            get { return data; }  
            set { data = value; }  
        }  
    }  

    private Node head;  

    public GenericList() //constructor  
    {  
        head = null;  
    }  

    public void AddHead(T t)  
    {  
        Node n = new Node(t);  
        n.Next = head;  
        head = n;  
    }  

    public IEnumerator<T> GetEnumerator()  
    {  
        Node current = head;  


        while (current != null)  
        {  
            yield return current.Data;  
            current = current.Next;  
        }  
    }  

    public T FindFirstOccurrence(string s)  
    {  
        Node current = head;  
        T t = null;  


        while (current != null)  
        {  
            //The constraint enables access to the Name property.  
            if (current.Data.Name == s)  
            {  
                t = current.Data;  
                break;  
            }  
            else  
            {  
                current = current.Next;  
            }  
        }  
        return t;  
    }  
}

经过约束类型参数,能够增长约束类型及其继承层次结构中的全部类型所支持的容许操做和方法调用的数量。所以,在设计泛型类或方法时,若是要对泛型成员执行除简单赋值以外的任何操做或调用 System.Object 不支持的任何方法,您将须要对该类型参数应用约束。
在应用 where T : class 约束时,建议不要对类型参数使用 == 和 != 运算符,由于这些运算符仅测试引用同一性而不测试值相等性。即便在用做参数的类型中重载这些运算符也是如此。下面的代码说明了这一点;即便 String 类重载 == 运算符,输出也为 false。接口

public static void OpTest<T>(T s, T t) where T : class  
{  
    System.Console.WriteLine(s == t);  
}  
static void Main()  
{  
    string s1 = "foo";  
    System.Text.StringBuilder sb = new System.Text.StringBuilder("foo");  
    string s2 = sb.ToString();  
    OpTest<string>(s1, s2);  
}

这种状况的缘由在于,编译器在编译时仅知道 T 是引用类型,所以必须使用对全部引用类型都有效的默认运算符。若是须要测试值相等性,建议的方法是同时应用 where T : IComparable 约束,并在将用于构造泛型类的任何类中实现该接口。
未绑定的类型参数

没有约束的类型参数(如公共类 SampleClass {} 中的 T)称为未绑定的类型参数。未绑定的类型参数具备如下规则:
•不能使用 != 和 == 运算符,由于没法保证具体类型参数能支持这些运算符。
•能够在它们与 System.Object 之间来回转换,或将它们显式转换为任何接口类型。
•能够将它们与 null 进行比较。将未绑定的参数与 null 进行比较时,若是类型参数为值类型,则该比较将始终返回 false。

裸类型约束

用做约束的泛型类型参数称为裸类型约束。当具备本身的类型参数的成员函数须要将该参数约束为包含类型的类型参数时,裸类型约束颇有用,以下面的示例所示:

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

在上面的示例中,T 在 Add 方法的上下文中是一个裸类型约束,而在 List 类的上下文中是一个未绑定的类型参数。
裸类型约束还能够在泛型类定义中使用。注意,还必须已经和其余任何类型参数一块儿在尖括号中声明了裸类型约束:

//naked type constraint  
public class SampleClass<T, U, V> where T : V { }

泛型类的裸类型约束的做用很是有限,由于编译器除了假设某个裸类型约束派生自 System.Object 之外,不会作其余任何假设。在但愿强制两个类型参数之间的继承关系的状况下,可对泛型类使用裸类型约束。

相关文章
相关标签/搜索