当泛型类或泛型方法被编译为微软中间语言(MSIL)后,它所包含的元数据定义了它的类型参数。根据所给的类型参数是值类型还是引用类型,对泛型类型所用的MSIL也是不同的。
当第一次以值类型作为参数来构造一个泛型类型,运行时用所提供的参数或在MSIL中适当位置被替换的参数,来创建一个专用的泛型类型。[JX3]
例如,假设你的程序代码声名一个由整型构成的栈,如:
Stack<int> stack;
此时,运行时用整型恰当地替换了它的类型参数,生成一个专用版本的栈。此后,程序代码再用到整型栈时,运行时复用已创建的专用的栈。下面的例子创建了两个整型栈的实例,它们共用一个Stack<int>代码实例:
Stack<int> stackOne = new Stack<int>(); Stack<int> stackTwo = new Stack<int>();
然而,如果由另一种值类型——如长整型或用户自定义的结构——作为参数,在代码的其他地方创建另一个栈,那么运行时会生成另一个版本的泛型类型。这次是把长整型替换到MSIL中的适当的位置。由于每个专用泛型类原本就包含值类型,因此不需要再转换。
对于引用类型,泛型的工作略有不同。当第一次用任何引用类型构造泛型类时,运行时在MSIL中创建一个专用泛型类,其中的参数被对象引用所替换。之后,每当用一个引用类型作为参数来实例化一个已构造类型时,就忽略其类型,运行时复用先前创建的专用版本的泛型类。这可能是由于所有的引用的大小都相同。
例如,假如你有两个引用类型,一个Customer类和一个Order类;进一步假设你创建了一个Customer的栈:
Stack<Customer> customers;
此时,运行时生成一个专用版本的栈,用于稍后存储对象的引用,而不是存储数据。假如下一行代码创建了一个另一种引用类型的栈,名为Order:
Stack<Order> orders = new Stack<Order>();
和值类型不同,运行时并没有为Order类型创建另一个栈的专用版本。相反,运行时创建了一个专用版本栈实例,并且变量orders指向这个实例。如果之后是一行创建Customer类型的栈的代码:
customers = new Stack<Customer>();
和之前以Order类型创建的栈一样,创建了专用栈的另一个实例,并且其中所包含的指针指向一块大小与Customer类一致的内存。由于不同程序间引用类型的数量差别很大,而编译器只为引用类型的泛型类创建一个专用类,因此C#对泛型的实现极大地降低了代码膨胀。
此外,当用类型参数实现一个泛型C#类时,想知道它是指类型还是引用类型,可以在运行时通过反射确定它的真实类型和它的类型参数。
来源:https://www.cnblogs.com/XiaoRuLiang/p/12422319.html