dotNET Generics
dotNET泛型
By Ivo Stoykov Generics are available since NET version 2.0 as a part of CLR types. Those might be classes, structures, interfaces, and methods which type specifications are declared and initialized in consumer code. One of the benefits of generics is increased code reusability and type safety. NET Framework has number of generic classes in System.Collections.ObjectM
从NET 2.0版开始,泛型是CLR类型的一部分。 这些可能是类型,结构,接口和方法,它们的类型规范是在使用者代码中声明和初始化的。 泛型的好处之一是提高了代码的可重用性和类型安全性。 NET Framework在
Generic sort and equity interfaces and delegate types are located in System.
通用排序和公平接口以及委托类型位于System中。
Generic classes
通用类
Generic class might use type parameter defining what objects it stores. Type parameters appear as type of its fields and methods.
泛型类可能使用类型参数来定义其存储的对象。 类型参数显示为其字段和方法的类型。
public class SimpleGenericClass<T>
{
public T val;
}
unbounded type.
Such a declaration is knows as a generic type definition and acts as a template.
这样的声明被称为
Looking in the body of the class we see the type “T” of the member variable “val”. It means that actual type of the variable will be specified during class construction as shown below:
在类的主体中,我们看到成员变量“ val”的类型“ T”。 这意味着将在类构造期间指定变量的实际类型,如下所示:
SimpleGenericClass<string> s = new SimpleGenericClass<string>(); // desired type - string in this case - is between brackets “<>”
s.val = “This is a string.”; // value is a string type
// s.val.GetType() will return System.String
SimpleGenericClass<int> s = new SimpleGenericClass<int>(); // type is int
s.val = “15”; // value is an int type
// s.val.GetType() will return System.Int32
Unbounded Type Parameters Limitation
无限类型参数限制
Generic or unbounded types give only the lowest minimum of features to the object to exist. It could use only these features supplied by System.Object. This is why developers have to consider following list of limitations which might face using unbounded type parameters:Do not support != and == because there is no guarantee that supplied type argument will support them.
不支持!=和==,因为不能保证提供的type参数将支持它们。
public class Test<T>
{
public bool Find<T>(T a, T b)
{
return (a == b);
}
}
When an attempt to build is made compiler will complain returning
尝试进行编译时,编译器将抱怨返回
Operator '==' cannot be applied to operands of type 'T' and 'T'
This is because compilation depends on the types supplied and during compilation it is not clear whether this action (== equation) will be supported by passed types or whether supplied types will be comparable.
这是因为编译取决于提供的类型,并且在编译期间尚不清楚传递的类型是否将支持此操作(==公式),或者提供的类型是否可比。
Value types do not provide an overload for == by default nevertheless most of the value types provided by the framework provide their own overload.
默认情况下,值类型不为==提供重载,但是框架提供的大多数值类型都提供了自己的重载。
Can be converted to and from System.Object, or explicitly to any interface type
可以与System.Object相互转换,也可以显式转换为任何接口类型
This means unconstrained type parameters are assumed to be of type System.Object. So less constraints applied, less resources are available. Using constraints for type parameters is recommended because it will make accessible all operations and method available to constraining type and those in inheritance hierarchy.
这意味着无约束类型参数被假定为
They are
他们是
null comparable. For arguments of value type will return always null可比。 对于值类型的参数将始终返回 false (because value type must be initialized before they are used and always will have a value). false (因为值类型必须在使用它们之前初始化并且始终具有值)。Constraints
约束条件
Generic parameters might be limited by constraints. They restrict acceptable types during object instantiation. Passing an object that does not match to allowed types will raise a compile-time error. Type constraints are defined with where contextual keyword. The syntax is:
通用参数可能会受到
public class SimpleGenericClass<T> where T: struct
{
}
Acceptable constraints are:
可接受的约束是:
struct – restrict to value types, except struct –限制为值类型,但 Nullable. Nullable除外。
class – restrict to 类 –限制为 reference type (any class, interface, delegate, or array type)
new() – require public parameterless constructor. It guarantees that type will have a public constructor without argument. This must be last in constraint list. new() –需要公共无参数构造函数。 它保证该类型将具有不带参数的公共构造函数。 这必须在约束列表的最后。
<baseclass> – type must be or derive from specified base class <baseclass> –类型必须是或衍生自指定的基类
<interface> – type must implement specified interface. Interface can be generic. Multiple interface constraints also can be specified <interface> –类型必须实现指定的接口。 接口可以是通用的。 也可以指定多个接口约束
U – type must derive from the U argument, also known as U –类型必须从U参数派生,也称为 naked type constraint.
public class SimpleGenericClass<T> where T: System.IComparable<T>, new()
{
}
public class SimpleGenericClass<T, U>
where T: struct
where U: Base, new()
{}
Without constraints only System.Object methods are supported. Applying constraints increase operators and methods to call to all types of inheritance hierarchy.
没有限制,仅支持
Generic methods
通用方法
A Generic method’s type parameter is used either for its return value or formal parameters. They are known as generic type parameters or simply type parameters in method definition. A method is generic only if it has its own list of type parameters as in following declaration:
泛型方法的类型参数用于其返回值或形式参数。 在方法定义中,它们称为
class StandardClass
{
T test<T>(T args) {. . .} // method is generic
}
To define generic methods, the type parameter syntax is added immediately after the method name. Without the type parameter, method is not generic nevertheless it is declared either into generic type or its formal parameters are generic:
为了定义通用方法,在方法名称之后立即添加类型参数语法。 没有类型参数,方法不是通用的,但是它被声明为通用类型,或者它的形式参数是通用的:
class Generic<T>
{
T test(T args) {. . .} // method is not generic
}
Overloaded methods which are often used to perform similar operations on different types of data might be coded as generic methods, which will produce more compact code.
经常用于对不同类型的数据执行类似操作的重载方法可能被编码为通用方法,这将产生更紧凑的代码。
How Generics works
泛型的工作原理
Compilation converts code into MSIL (Microsoft intermediate language) and creates a portable executable file (PE) together with metadata as generics information, attributes etc. Metadata is used during runtime for different purposes like application control, data processing, etc. For generic types, metadata identifies existence of type parameters and if found, the runtime creates a specialized generic type. When this specialized type is first constructed, runtime uses supplied parameter types and replaces appropriate code in MSIL.
编译将代码转换为MSIL(Microsoft中间语言),并与元数据一起作为通用信息,属性等创建可移植的可执行文件(PE)。元数据在运行时用于各种目的,例如应用程序控制,数据处理等。对于通用类型,元数据标识类型参数的存在,如果找到,运行时将创建特殊的通用类型。 首次构造此特殊类型时,运行时将使用提供的参数类型并替换MSIL中的相应代码。
Depending on supplied parameters, value or reference type, MSIL is differently used.
根据提供的参数, 值或参考类型,使用不同的MSIL。
For value type parameters runtime creates specialized type for each unique parameter. For example, if there is a list of integer declared as in code below a specialized version of integer list class will be created.
对于值类型参数,运行时将为每个唯一参数创建专用类型。 例如,如果下面的代码中声明了一个整数列表,则将创建整数列表类的专用版本。
List<int> lst;
Then consider the following code:
然后考虑以下代码:
List<int> listOne = new List<int>();
List<int> listTwo = new List<int>();
Because supplied parameters in both cases are integers, runtime will reuse specialized List class it has generated when int parameter has been supplied. So each instantiated integer List class will share the single specialize instance of List code that the runtime has produced.
因为在两种情况下提供的参数都是整数,所以运行时将重用在提供int参数时生成的专用List类。 因此,每个实例化的整数List类将共享运行时生成的List代码的单个特殊实例。
List<int> listOne = new List<int>();
List<double> listTwo = new List<double>();
When another value type - let’s say double as in code above - is passed for creating a List object, runtime will create another version of specialized type replacing appropriate place in MSIL code to suit supplied double type.
当传递另一个值类型(例如,上面代码中的double值)以创建List对象时,运行时将创建另一个版本的专用类型,以替换MSIL代码中的适当位置以适合提供的double类型。
When parameter is reference type runtime creates specialized generic type and replaces given parameter in MSIL with object reference. For each instance that has reference type as parameter, runtime reuses previously created version of specialized generic type. This always happens nevertheless what the type is as long as it is reference type.
当参数为引用类型时,运行时将创建专门的泛型类型,并将MSIL中的给定参数替换为对象引用。 对于将引用类型作为参数的每个实例,运行时将重用以前创建的特殊泛型类型的版本。 但是,只要引用类型是类型,就始终会发生这种情况。
For example if we have two classes (reference types) Order and Customer and we have following code:
例如,如果我们有两个类(引用类型)Order和Customer,并且我们有以下代码:
class Customer {}
class Order {}
List<Customer> customer;
List<Order> orders = new List<Order>();
customer = new List<Customer>();
In line 3, the runtime will create specialized type with reference to Customer object without storing any data. In line 4, runtime will reuse the created specialized version of List class to create the new instance instead of creating new one as with value types. Reference is change to Order type.
在第3行中 ,运行时将参考Customer对象创建专用类型,而不存储任何数据。 在第4行中 ,运行时将重用List类的已创建专用版本来创建新实例,而不是使用值类型来创建新实例。 参考是对订单类型的更改。
In last line new instance is created using the same specialized type but with reference to Customer object.
在最后一行中,使用相同的特殊类型创建了新实例,但引用了Customer对象。
This means that the C# implementation of generics reduces the code by reducing created by the compiler specialized classes to one.
这意味着泛型的C#实现通过将编译器专用类创建的类减少为一个来减少代码。
Microsoft recommends using System.Collections.Generic
Microsoft建议使用
Conclusion
结论
Generics help to write more reusable and compact code, reduce overloading. Replacing non-generic classes with their generic counterparts is recommended.
泛型有助于编写更多可重用和紧凑的代码,减少重载。 建议用非常规类替换其非常规类。
More reading
更多阅读
An Introduction to C# Generics C#泛型简介 Generics FAQ: .NET Framework 泛型常见问题解答:.NET Framework翻译自: https://www.experts-exchange.com/articles/3199/Generics-in-dotNET.html