您的位置:首页 > 编程语言 > C#

C# 泛型类型参数的约束

2017-03-31 21:58 489 查看

C# 泛型类型参数的约束

http://www.cnblogs.com/rinack/p/5676311.html

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

where T: struct

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。有关更多信息,请参见使用可以为 null 的类型(C# 编程指南)。

where T : class

类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。

where T:new()

类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。

where T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

where T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

where T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

使用约束的原因  

 

如果要检查泛型列表中的某个项以确定它是否有效,或者将它与其他某个项进行比较,则编译器必须在一定程度上保证它需要调用的运算符或方法将受到客户端代码可能指定的任何类型参数的支持。这种保证是通过对泛型类定义应用一个或多个约束获得的。例如,基类约束告诉编译器:仅此类型的对象或从此类型派生的对象才可用作类型参数。一旦编译器有了这个保证,它就能够允许在泛型类中调用该类型的方法。约束是使用上下文关键字 where 应用的。

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


约束使得泛型类能够使用 Employee.Name 属性,因为类型为 T 的所有项都保证是 Employee 对象或从 Employee 继承的对象。

可以对同一类型参数应用多个约束,并且约束自身可以是泛型类型,如下所示:

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

{

    // ...

}

 

通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所支持的允许操作和方法调用的数量。因此,在设计泛型类或方法时,如果要对泛型成员执行除简单赋值之外的任何操作或调用 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 = "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 以外,不会做其他任何假设。在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使用参数类型约束。

========

泛型类型参数及约束

http://www.cnblogs.com/wanghonghu/archive/2012/07/11/2586920.html

泛型类型参数简介

    在定义泛型类型和泛型方法时,常用到泛型类型参数,泛型类型参数是在实例化泛型时指定类型的占位符。泛型类型参数放在“<>”内。

    泛型类型参数命名建议:

    (1)当泛型类型参数为单个字母时,建议用T表示。

    (1)当泛型类型参数用单词定义时,建议在单词前加T。

private void PromptName<T>(T t) {}

private void PromptName<Tuser>(Tuser user){}

泛型类型参数约束

      在定义泛型类时,可以对在实例化泛型类时用于类型参数的类型种类施加限制。如果实例化泛型类时使用某个约束所不允许的类型来实例化类,则会产生编译时错误。

      泛型约束分类:  

约束

说明

T:结构

类型参数必须是值类型。 可以指定除 Nullable 以外的任何值类型。

T:类

类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。

T:new()

类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。

T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。

T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

 (1)类型参数约束为结构(struct)。

public class ShowObjectType<T> where T : struct
{
public void ShowValue<T>(T t)
{
Console.WriteLine(t.GetType());
}
}

class GenericConstraint
{
static void Main()
{
ShowObjectType<int> showInt = new ShowObjectType<int>();
showInt.ShowValue<int>(5);
showInt.ShowValue(5);//从参数可以推导出类型参数类型,则可以省略类型参数类型

//因为约束为值类型,下面代码不能通过编译
ShowObjectType<string> showString = new ShowObjectType<string>();
showString.ShowValue("5");

Console.Read();
}
}


   (2)类型参数约束为类(class)。

    在应用 where T : class 约束时,避免对类型参数使用 == 和 != 运算符,因为这些运算符仅测试类型为引用类型,而不测试值相等性。

class GenericConstraint
{
static void Main()
{
List<string > list = new List<string>();
AddClass<string>(list, "hello generic");

Console.Read();
}

private static void AddClass<T>(List<T> list, T t) where T : class
{
list.Add(t);
}
}


  (4)类型参数约束为具体类。

         约束为具体类时,可利用类型参数调用具体类的属性和方法。

    class GenericConstraint

    {

        static void Main()

        {

            Person person = new Person { ID = 1, Name = "David" };

            PromptName<Person>(person);

            Console.Read();

        }

        //此约束T为Person对象或者继承Person对象

        private static void PromptName<T>(T t) where T : Person 

        {

            //此处可使用Person的Name属性

            if (t.Name == "David")

            {

                Console.WriteLine("Person name is David");

            }

            string name = t.GetName();

            Console.WriteLine("Person name is {0}", name);

        }

    }

    public class Person

    {

        private int id;

        public int ID

        {

            get { return id; }

            set { id = value; }

        }

        private string name;

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

        public string GetName()

        {

            return Name;

        }

    }

复制代码

    (5)约束多个参数。

class Base { }

class Test<T, U>

    where U : struct

    where T : Base, new() { }

    (6)未绑定类型参数。

      没有约束的类型参数,称为未绑定的类型参数。

class  List<T>{}

========

泛型类型参数约束 <T> where T : class,new(){}

.NET支持的类型参数约束有以下五种:

where T : struct                               | T必须是一个结构类型

where T : class                               | T必须是一个类(class)类型,不能是结构(structure)类型   引用类型

where T : new()                               | T必须要有一个无参构造函数    可以被new()

where T : NameOfBaseClass          | T必须继承名为NameOfBaseClass的类

where T : NameOfInterface             | T必须实现名为NameOfInterface的接口

========

where(泛型类型约束)

http://www.cnblogs.com/lystory/p/5104865.html

定义:在定义泛型的时候,我们可以使用 where 限制参数的范围。

使用:在使用泛型的时候,你必须尊守 where 限制参数的范围,否则编译不会通过。

六种类型的约束:

T:类(类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。)

    class MyClass<T, U>

        where T : class///约束T参数必须为“引用 类型{ }”

        where U : struct///约束U参数必须为“值 类型”

    { }

T:结构(类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。)

    class MyClass<T, U>

        where T : class///约束T参数必须为“引用 类型{ }”

        where U : struct///约束U参数必须为“值 类型”

    { }

T:new()(类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。)

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

{

    // ...

}

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

public class Employee{}

public class GenericList<T> where T : Employee

T:<接口名称>(类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。)

复制代码

    /// <summary>

    /// 接口

    /// </summary>

    interface IMyInterface

    {

    }

    /// <summary>

    /// 定义的一个字典类型

    /// </summary>

    /// <typeparam name="TKey"></typeparam>

    /// <typeparam name="TVal"></typeparam>

    class Dictionary<TKey, TVal>

        where TKey : IComparable, IEnumerable

        where TVal : IMyInterface

    {

        public void Add(TKey key, TVal val)

        {

        }

    }

复制代码

T:U(为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。也就是说T和U的参数必须一样)

class List<T>

{

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

}

 

一、可用于类:

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

二、可用于方法:

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

三、可用于委托:

delegate T MyDelegate<T>() where T : new()

 

在实际项目中什么时候用到它们?

有时候你在做一个项目的时候,你需要用到泛型,你只希望传给你的泛型参数是限定范围的,

比如你希望值类型,或者是引用类型,或者是继承至某个类型、或者是符合某个接扣的类型,

这个时候你该如何办?你就需要用到 WHERE 来限定了。

 

参考文档:
https://msdn.microsoft.com/zh-cn/library/d5x73970.aspx https://msdn.microsoft.com/zh-cn/library/bb384067.aspx
========

c# 泛型类型参数与约束的深入分析

http://www.jb51.net/article/37657.htm

本篇文章是对c#中泛型类型参数与约束进行了详细的分析介绍,需要的朋友参考下

..泛型类型参数简介

在定义泛型类型和泛型方法时,常用到泛型类型参数,泛型类型参数是在实例化泛型时指定类型的占位符。泛型类型参数放在“<>”内。

泛型类型参数命名建议:

(1)当泛型类型参数为单个字母时,建议用T表示。

(2)当泛型类型参数用单词定义时,建议在单词前加T。

复制代码 代码如下:

private void PromptName<T>(T t) {}

private void PromptName<Tuser>(Tuser user){}

泛型类型参数约束

在定义泛型类时,可以对在实例化泛型类时用于类型参数的类型种类施加限制。如果实例化泛型类时使用某个约束所不允许的类型来实例化类,则会产生编译时错误。

泛型约束分类:  

约束

 说明

 

T:结构

 类型参数必须是值类型。 可以指定除 Nullable 以外的任何值类型。 

 

T:类

 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。

 

T:new()

 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。

 

T:<基类名>

 类型参数必须是指定的基类或派生自指定的基类。

 

T:<接口名称>

 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。

 

T:U

 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

 

(1)类型参数约束为结构(struct)。

复制代码 代码如下:

public class ShowObjectType<T> where T : struct

    {

        public void ShowValue<T>(T t)

        {

            Console.WriteLine(t.GetType());

        }

    }

    class GenericConstraint

    {

        static void Main()

        {

            ShowObjectType<int> showInt = new ShowObjectType<int>();

            showInt.ShowValue<int>(5);

            showInt.ShowValue(5);//从参数可以推导出类型参数类型,则可以省略类型参数类型

            //因为约束为值类型,下面代码不能通过编译

            ShowObjectType<string> showString = new ShowObjectType<string>();

            showString.ShowValue("5");

            Console.Read();

        }

    }

(2)类型参数约束为类(class)。

在应用 where T : class 约束时,避免对类型参数使用 == 和 != 运算符,因为这些运算符仅测试类型为引用类型,而不测试值相等性。

复制代码 代码如下:

class GenericConstraint

    {

        static void Main()

        {

              List<string > list = new List<string>();

            AddClass<string>(list, "hello generic");

            Console.Read();

        }

        private static void AddClass<T>(List<T> list, T t) where T : class

        {

            list.Add(t);

        }

    }

(3)类型参数约束为具体类。

约束为具体类时,可利用类型参数调用具体类的属性和方法。

复制代码 代码如下:

class GenericConstraint

    {

        static void Main()

        {

            Person person = new Person { ID = 1, Name = "David" };

            PromptName<Person>(person);

            Console.Read();

        }

        //此约束T为Person对象或者继承Person对象

        private static void PromptName<T>(T t) where T : Person 

        {

            //此处可使用Person的Name属性

            if (t.Name == "David")

            {

                Console.WriteLine("Person name is David");

            }

            string name = t.GetName();

            Console.WriteLine("Person name is {0}", name);

        }

    }

    public class Person

    {

        private int id;

        public int ID

        {

            get { return id; }

            set { id = value; }

        }

        private string name;

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

        public string GetName()

        {

            return Name;

        }

    }

(4)约束多个参数。

复制代码 代码如下:

class Base { }

class Test<T, U>

    where U : struct

    where T : Base, new() { }

(5)未绑定类型参数。

 没有约束的类型参数,称为未绑定的类型参数。

复制代码 代码如下:

class  List<T>{}

========
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  泛型 c#