在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 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 以外,不会做其他任何假设。在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使用参数类型约束。

C# 泛型类型参数的约束的更多相关文章

  1. [转] C# 泛型类型参数的约束

    啊.紫原文C# 泛型类型参数的约束 在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制.如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误.这些限制 ...

  2. 转载c#泛型 类型参数的约束(c#编程指南)

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

  3. where 泛型类型参数及约束

    private void InsertData<TRowMetadata, TFieldMetadata, TCellMetadata>(IMetadataReader<TRowMe ...

  4. 编写高质量代码改善C#程序的157个建议[优先考虑泛型、避免在泛型中声明静态成员、为泛型参数设定约束]

    前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托 ...

  5. C# 类型参数的约束

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

  6. 编写高质量代码改善C#程序的157个建议——建议34:为泛型参数设定约束

    建议34:为泛型参数设定约束 “约束”这个词可能会引起歧义,有些人肯能认为对泛型参数设定约束是限制参数的使用,实际情况正好相反.没有“约束”的泛型参数作用很有限,倒是“约束”让泛型参数具有了更多的行为 ...

  7. 泛型中new()约束的用法

    一..NET中支持的类型参数约束有以下几种 where T : struct              T必须是一个结构类型where T : class               T必须是一个类( ...

  8. 编写高质量代码改善C#程序的157个建议——建议129:泛型类型参数要以T作为前缀

    建议129:泛型类型参数要以T作为前缀 作为一种约定,泛型类型的参数要以T作为前缀.如委托声明: Action<T1,T2> 其中,泛型类型参数名不应该处理成: Action<Arg ...

  9. 编写高质量代码改善C#程序的157个建议——建议45:为泛型类型参数指定逆变

    建议45:为泛型类型参数指定逆变 逆变是指方法的参数可以是委托或者泛型接口的参数类型的基类.FCL4.0中支持逆变的常用委托有: Func<int T,out TResult> Predi ...

随机推荐

  1. 【OpenGL ES】关于VBO(Vertex Buffer Object)的一些坑——解析一些关于glBuffer的函数

    最近在写毕设的时候用到OpenGL ES中的VBO,由于对一些接口用到的变量不了解被坑得很惨,在此记录一下防止以后再被坑. 本文为大便一箩筐的原创内容,转载请注明出处,谢谢:http://www.cn ...

  2. 【spark】IDEA建立基于scala语言的spark项目

    1.新建一个Spark项目 2.选择maven,用模板创建项目 如果 没有这个模板,我们需要添加一个 我们这里使用的是1.6版本Archetype Group Id : net.alchim31.ma ...

  3. msbuild编译

    @echo offcd /d %~dp0 if exist output.log del /q /f output.logif exist success.txt del /q /f success. ...

  4. SQLSERVER store procedure 临时表

    有些时候显示重复数据时,使用: ) 但有些时候表A过大或者逻辑复杂.显示数据时,会造成性能的影响,这时你就可以使用临时表:   ) create table #temp( XXX , XXX) )in ...

  5. LoadRunner压力测试实际运用的使用方法

    LoadRunner 是一种预测系统行为和性能的工业标准级负载测试工具.通过以模拟上 千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner 能够对整个 企业架构进行测试. 方 ...

  6. <input type="checkbox" name="" value=" ">

    <input type="checkbox"  name=""  value=" ">

  7. L126

    Like so many things, it is not what's outside, but what is inside that counts. 许多事物都是如此,外表看起來虽不起眼,但是 ...

  8. Mongodb 的劣势

    MongoDB中的数据存放具有相当的随意性,不具有MySQL在开始就定义好了.对运维人员来说,他们可能不清楚数据库内部数据的数据格式,这也会数据库的运维带来了麻烦. 1. 事务关系支持薄弱.这也是所有 ...

  9. Python标准库之time和datetime

    注:博客转载自:https://www.cnblogs.com/zhangxinqi/p/7687862.html 1.python3日期和时间 Python 程序能用很多方式处理日期和时间,转换日期 ...

  10. SCARA——OpenGL入门学习五六(三维变换、动画)

    OpenGL入门学习(五) 此课为三维变换的内容,比较枯燥.主要是因为很多函数在单独使用时都不好描述其效果, 在前面绘制几何图形的时候,大家是否觉得我们绘图的范围太狭隘了呢?坐标只能从-1到1,还只能 ...