CLR via c#读书笔记八:泛型

1、定义泛型类型或方法时,为类型指定的任何变量(比如T)都称为类型参数。使用泛型类型或方法时指定的具体数据类型称为类型实参

2、System.Collections.Concurrent命名空间提供了线程安全的泛型集合类。Microsoft建议使用泛型集合类,不建议使用非泛型集合类。

3、具有泛型类型参数的类型称为开放类型,CLR禁止构造开放类型的任何实例。这类似于CLR禁止构造接口类型的实例。代码引用泛型类型时可指定一组泛型类型实参。为所有类型参数都传递了实际的数据类型,类型就成为封闭类型。CLR允许构造封闭类型的实例。

4、每个封闭类型都有自己的静态字段。换言之,假如List<T>定义了任何静态字段,这些字段不会在一个List<DateTime>和一个List<String>之间共享;每个封闭类型对象都有自己的静态字段。

5、泛型类型定义静态构造器的目的是保证传递的类型实参满足特定条件。例如,我们可以向下面这样定义只能处理枚举类型的泛型类型:

internal sealed class GenericTypeThatRequiresAnEnum<T>{
    static GenericTypeThatRequiresAnEnum(){
    if(!typeof(T).IsEnum){
      throw new ArgumentException("T must be an enumerated type");
    }
  } }

  CLR提供了名为约束的功能,可以更好的指定有效的类型参数。遗憾的是,约束无法将类型实参限制为“仅枚举类型”。正是因为这个原因,所以上例需要用静态构造器来保证类型是一个枚举类型。

6、泛型类型仍然是类型,所有能从其他任何类型派生。使用泛型类型指定类型实参时,实际是在CLR中定义一个新的类型对象,新的类型对象从泛型类型派生自的那个类型派生。

7、泛型类型同一性:C#允许使用简化的语法来引用泛型封闭类型,同时不会影响类型的相等性,类如:

using DateTimeList=System.Collections.Generic.List<System.DateTime>;

  using指令实际定义的是名为DateTimeList的符号,代码编译时,编译器将代码中出现的所有DateTimeList替换成System.Collections.Generic.List<System.DateTime>。这样就允许开发人员使用简化的语法,同时不影响代码的实际含义。

8、CLR要为每种不同的方法/类型组合生成本机代码。这个现象称为代码爆炸。幸好,CLR内建了一些优化措施能缓解代码爆炸。首先,假如为特定的类型实参调用了一个方法,以后再用相同的类型实参调用这个方法,CLR只会为这个方法/组合编译一次代码,同一个AppDomain中能共享不同程序集编译的相同泛型类型。另一个优化,它认为所有引用类型实参都完全相同,所以代码能够共享。例如,CLR为List<String>的方法编译的代码可直接用于List<Stream>的方法,因为String和Stream均为引用类型。事实上,对于任何引用类型,都会使用相同的代码,CLR之所以能执行这个优化,是因为所有引用类型的实参和变量实际只是指向堆上对象的指针(32位系统上是32位指针,64位系统上是64位指针),而所有对象指针都以相同方式操纵。但是类型实参是值类型,CLR 就必须专门为那个值类型生成本机代码。因为值类型的大小不定。

9、泛型接口

10、泛型委托

  CLR支持泛型委托,目的是保证任何类型的对象都能以类型安全的方式传给回调方法。此外,泛型委托允许值类型实例在传给回调方法时不进行任何装箱。

11、委托和接口的逆变和协变泛型类型实参

  不变量(invariant)意味着泛型类型参数不能更改。

  逆变量(contravariant)意味着泛型类型参数可以从一个类更改为它的某个派生类。在C#是用in关键字标记逆变量形式的泛型类型参数。逆变量泛型类型参数只出现在输入位置,比如作为方法的参数。

  协变量(covariant)意味着泛型类型参数可以从一个类更改为它的某个基类。c#是用out关键字标记协变量形式的泛型类型参数。协变量泛型类型参数只能出现在输出位置,比如作为方法的返回类型。

12、泛型方法

  CLR还允许方法指定它自己的类型参数。这些类型参数可以作为参数、返回值和局部变量的类型使用。

  

internal sealed class GenericType(T){
   private T m_value;
  public GenericType(T value){ m_value=value; }
  public TOutput Converter<TOutput>(){
    TOutput result=(TOutput)Convert.ChangeType(m_value,typeof(TOutput));
    return result;//返回类型转换之后的结果
  }
}

  这个例子类型参数是T,方法定义了自己的类型参数TOutput。

13、可验证性和约束

  约束

public static T Min<T>(T o1,T o2) where T:IComparable<T>{
  if(o1.CompareTo(o2)<0) return o1;
  return o2;
}

  C#的where关键字告诉编译器,为T指定的任何类型都必须实现同类型(T)的泛型IComparable接口。

  重写虚泛型方法时,重写的方法必须指定相同数量的类型参数,而且这些类型参数会继承在基类方法上指定的约束。事实上,根本就不允许为重写方法的类型参数指定任何约束。但类型参数的名称是可以改变的。

14、主要约束

  类型参数可以指定零个或者一个主要约束。主要约束可以是代表非密封类的一个引用类型。不能指定一下特殊引用类型:System.Object,System.Array,System.Delegate,System.multicastDelegate,System.ValueType,System.Enum或者System.Void。

  指定引用类型约束,相当于向编译器承诺:一个指定的类型实参要么是与约束类型相同的类型,要么是从约束类型派生的类型。

  有两个特殊的主要约束:class和struct。其中,class约束向编译器承诺类型实参是引用类型。任何类型、接口类型、委托类型或者数组类型都满足这个约束。struct约束向编译器承诺类型实参是值类型。

15、所有值类型都隐式地有一个公共无参构造器。

16、次要约束

  类型参数可以指定零个或者多个次要约束,次要约束代表接口类型。

  还有一个次要约束成为类型参数约束,有时也称为裸类型约束。它允许一个泛型类型或方法规定:指定的类型实参要么是约束的类型,要么是约束的类型的派生类。

17、构造器约束

  类型参数可指定零个或一个构造器约束,它向编译器承诺类型实参是实现了公共无参构造器的非抽象类型。

  

internal sealed class ConstructorConstraint<T> where T:new(){
    public static T Factory(){
        //允许,因为所有值类型都隐式有一个公共无参构造器。
        //而如果指定的是引用类型,约束也要求它提供公共无参构造器
        return New T();
    }
}
原文地址:https://www.cnblogs.com/yuzhoumanwu/p/9553048.html