C# 泛型类型约束

C#泛型是使用的最为广泛的一个特性,在基础框架中四处可见它的身影,泛型将大量C#1当中的安全检查由运行时转换为了编译时执行,在C#1当中,容器ArrayList则是一个代表,他并不是类型安全的,每次从中读取数据,都需要进行一次强制类型转换或者装箱/拆箱操作,引发的性能问题与代码问题数不胜数。
在C#2当中,泛型的引入解决了这一问题,它将类型参数化,如同函数参数一样,类型参数从属于类型,而非从属于某个特定的构造函数。
在C#当中,例如已经提供了类型实参的泛型类型则是已构造类型,没有提供的则为未绑定泛型类型。

在其中,泛型定义的时候为我们提供了多种泛型约束方法,有以下四种:

  1. 引用类型约束。
  2. 值类型约束。
  3. 构造函数类型约束。
  4. 转换类型约束。

引用类型约束,如下:

struct RefSample<T> where T : class

有效的封闭类型:

RefSample<IDisposable>;
RefSample<string>;
RefSample<int[]>;

无效的封闭类型:

RefSample<int>;
RefSample<Guid>;

当这样约束了一个类型参数的时候,可以对类型参数进行==,!=来比较引用。

值类型约束

class ValSample<T> where T : struct

有效的封闭类型:

ValSample<int>;
ValSample<FileMode>; // 可为枚举

无效的封闭类型:

ValSample<object>;

当类型参数约束为值类型的时候,无法通过!=,==来进行比较。

构造函数约束

public T CreateInstance<T> where T : new()
{
    return new T();
}

本约束必须是所有类型参数的最后一个约束,它将会检查类型实参是否有一个可用于创建类型实例的无参构造函数。
例如以下调用,是有效的:

int a = CreateInstance<int>();

但是string是没有无参构造函数的,所以以下调用无效:

string str = CreateInstance<string>();

转换类型约束

本泛型约束是最为复杂的一种泛型约束,类型实参必须通过一致性引用或者装箱转换隐式地转换为该类型。
例如一下几个例子:

一致性转换
class Sample<T> where T : Stream
// 有效-一致性转换
Sample<Stream>
// 无效
Sample<string>
引用转换
struct Sample<T> where T : IDisposable
// 有效
Sample<SqlConnection>
// 无效
Sample<StringBuilder>
装箱转换
class Sample<T> where T : IComparable<T>
// 有效
Sample<int> // 由于int是实现了IComparable接口的
// 无效
Sample<FileInfo>
引用转换
class Sample<T,U> where T : U
// 有效
Sample<Stream,IDisposable>
// 无效
Sample<string,IDisposable>

转换类型约束可以指定多个接口,但是只能指定一个类,并且指定的类不可以是结构,封闭类,或者以下几种类型:

  • System.Object
  • System.Enum;
  • System.ValueType;
  • System.Delegate;

组合类型约束

注意,值类型由于已经有了一个无参构造函数,所以不允许指定一个构造函数约束,不过T即便被约束一个成一个值类型,他仍在可以在代码内使用new T();
如果有由多个类型转换约束,那么类应该出现在接口的前面。

//有效约束
class Sample<T> where T : class,IDisposable,new()
class Sample<T> where T : struct,IDisposable
class Sample<T,U> where T : class where : U struct,T
class Sample<T,U> where T : Stream where U : IDsposable
//无效约束
class Sample<T> where T : class,struct //不能又是值类型约束又是引用类型约束
class Sample<T> where T : Stream,class
class Sample<T> where T : new(),Stream //构造函数约束只能在最末位
class Sample<T> where T : IDisposable,Stream //类应该在接口前
class Sample<T> where T : XmlReader,IComparable,IComparable // 重复约束
class Sample<T> where T : struct where U : class,T
class Sample<T,U> where T : struct where U : class,T
class Sample<T,U> where T : Stream , U : IDisposable

组合约束可以分为主要约束(引用类型约束,值类型约束或使用类的转换类型约束),次要约束为使用接口或者其他类型参数的转换类型约束。

主要类型约束是可选的,但是只能有一个,次要类型约束可以有多个。