C # generic type parameter constraints of

Original: C # generic type parameter constraint

When defining generic class, the client may be able to impose restrictions on the type of the code kind type parameter when the class is instantiated. If the client code tries to use a type of constraint does not allow the class to instantiate, compile-time error results. These limits are called constraints. Constraints are using  where  contextual keyword specified. The following table lists the six types of constraints:

WHERE T: struct 
type parameter must be a value type. You can specify any value other than the type of Nullable. For more information, see use can be null (C # Programming Guide) type.
WHERE T: class 
type parameter must be a reference type; this also applies to any classes, interfaces, delegates, or array type. 
WHERE T: new new () 
type parameter must have a public constructor with no arguments. When used in conjunction with other constraints, new new () constraint must be specified last.
WHERE T: <base class name> 
type parameter must be specified in the base class is derived from the base class, or specified. 
WHERE T: <Interface name> 
Type parameter must be specified interface or implement the specified interface. Constraints can specify multiple interfaces. Constraints interface may also be generic. 
the WHERE T: U 
type parameter T must be provided for the parameters from U to U offers provided by or derived parameters.

If you want to check an item in a generic list to determine whether it is valid or compare it with some other items, the compiler must guarantee a certain extent, the operator needs to call or method it will be subject to client code It may support any type parameters specified. This is to ensure that the generic class defined by applying one or more constraints obtained. For example, the base class constraints tell the compiler: this type of object or objects derived from this type only was used as a type parameter. Once the compiler has the guarantee, it is possible to allow this type of method calls the generic class. Constraints are using contextual keyword  where  application.

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;
    }
}

Constraint that generic class can be used Employee.Name property, because all items of type T Employee object or guaranteed to be inherited from the Employee object.

It can be applied to a plurality of parameters of the same type constraint, and the constraint itself may be a generic type, as follows:

class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

 

By constraining the type parameter, you can increase the number of operations and methods allow invocation of all types of constraint types and their inheritance hierarchy supported. Therefore, when designing a generic class or method, if you want to do anything other than simple assignment of a member or call the generic  System.Object  any method that is not supported, you will need to apply constraints to the type parameters.

在应用 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 = "target";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
    string s2 = sb.ToString();
    OpTest<string>(s1, s2);
}

这种情况的原因在于,编译器在编译时仅知道 T 是引用类型,因此必须使用对所有引用类型都有效的默认运算符。如果必须测试值相等性,建议的方法是同时应用 where T : IComparable<T> 约束,并在将用于构造泛型类的任何类中实现该接口。

约束多个参数

可以对多个参数应用约束,并对一个参数应用多个约束,如下面的示例所示:

class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new() { }

未绑定的类型参数

没有约束的类型参数(如公共类 SampleClass<T>{} 中的 T)称为未绑定的类型参数。未绑定的类型参数具有以下规则:

不能使用 != 和 == 运算符,因为无法保证具体类型参数能支持这些运算符。

可以在它们与 System.Object 之间来回转换,或将它们显式转换为任何接口类型。

可以将它们与 null 进行比较。将未绑定的参数与 null 进行比较时,如果类型参数为值类型,则该比较将始终返回 false。

作为约束的类型参数

将泛型类型参数作为约束使用,在具有自己类型参数的成员函数必须将该参数约束为包含类型的类型参数时非常有用,如下示例所示:

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

在上面的示例中,T 在 Add 方法的上下文中是一个类型约束,而在 List 类的上下文中是一个未绑定的类型参数。

类型参数还可在泛型类定义中用作约束。请注意,必须在尖括号中声明此类型参数与任何其他类型的参数:

//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }

泛型类的类型参数约束的作用非常有限,因为编译器除了假设类型参数派生自 System.Object 以外,不会做其他任何假设。在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使用参数类型约束。

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/11286113.html