运行库中的C# 泛型讲的就是将C# 泛型类型或方法编译为 Microsoft 中间语言 (MSIL) 时,它包含将其标识为具有类型参数的元数据。C# 泛型类型的 MSIL 的使用因所提供的类型参数是值类型还是引用类型而不同。***次用值类型作为参数来构造泛型类型时,运行库会创建专用泛型类型,将提供的参数代入到 MSIL 中的适当位置。对于每个用作参数的***值类型,都会创建一次专用C# 泛型类型。
C# 泛型应用实例:
例如,假设您的程序代码声明了一个由整数构造的堆栈,如下所示:
- Stack<int> stack;
在此位置,运行库生成 Stack <T> 类的专用版本,并相应地用整数替换其参数。现在,只要程序代码使用整数堆栈,运行库就会重用生成的专用 Stack<T> 类。在下面的示例中,创建了整数堆栈的两个实例,它们共享 Stack<int> 代码的单个实例:
- Stack<int> stackOne = new Stack<int>();
- Stack<int> stackTwo = new Stack<int>();
但是,如果在程序代码中的其他位置创建了另一个 Stack<T> 类,这次使用不同的值类型(如 long 或用户定义的结构)作为其参数,则运行库会生成泛型类型的另一版本(这次将在 MSIL 中的适当位置代入 long)。由于每个专用泛型类本身就包含值类型,因此不再需要转换。
对于引用类型,C# 泛型的工作方式略有不同。***次使用任何引用类型构造泛型类型时,运行库会创建专用泛型类型,用对象引用替换 MSIL 中的参数。然后,每次使用引用类型作为参数来实例化构造类型时,无论引用类型的具体类型是什么,运行库都会重用以前创建的泛型类型的专用版本。之所以可以这样,是因为所有引用的大小相同。
例如,假设您有两个引用类型:一个 Customer 类和一个 Order 类,并且进一步假设您创建了一个 Customer 类型的堆栈:
- class Customer { }
- class Order { }
- Stack<Customer> customers;
在此情况下,运行库生成 Stack<T> 类的一个专用版本,该版本不是存储数据,而是存储稍后将填写的对象引用。假设下一行代码创建另一个引用类型的堆栈,称为 Order:
- Stack<Order> orders = new Stack<Order>();
不同于值类型,对于 Order 类型不创建 Stack<T> 类的另一个专用版本。而是创建 Stack<T> 类的一个专用版本实例,并将 orders 变量设置为引用它。假设接下来您遇到一行创建 Customer 类型堆栈:
C# 泛型代码:
- customers = new Stack<Customer>();
与前面使用 Order 类型创建的 Stack<T> 类一样,创建了专用 Stack<T> 类的另一个实例,并且其中所包含的指针被设置为引用 Customer 类型大小的内存区域。因为引用类型的数量会随程序的不同而大幅变化,C# 泛型实现将编译器为引用类型的泛型类创建的专用类的数量减小到一个,从而大幅减小代码量的增加。
此外,使用类型参数实例化泛型 C# 类时,无论它是值类型还是引用类型,可以在运行时使用反射查询它,并且可以确定它的实际类型和类型参数。
运行库中的C# 泛型的基本内容就向你介绍到这里,希望对你了解和学习运行库中的C# 泛型有所帮助。
【编辑推荐】