一、C#中的泛型引入了类型参数的概念,类似于C++中的模板,类型参数可以使类型或方法中的一个或多个类型的指定推迟到实例化或调用时,使用泛型可以更大程度的重用代码、保护类型安全性并提高性能;可以创建自定义的泛型类型(类、结构、接口、委托)和泛型方法;
1.在泛型类型的定义或泛型方法的声明中,类型参数是类型的占位符,这些占位符指代的类型需要在实例化泛型类型或调用泛型方法时进行指定;
※类型参数一般以T命名,如果是多个,使用T、U、V等,如果有指定约束,可以结合约束命名,例如需要继承自MyClass的类型参数命名为TMyClass;任何命名都并不会给类型参数增加额外作用;
2.泛型是运行时起作用的一套机制,根据运行时类型参数被指定为值类型还是引用类型其使用方式有所不同:
※当类型参数被指定为值类型时,会在第一次指定该特定值类型的类型时创建该类型唯一的专用化泛型类型,泛型类型中的类型参数会被替换为相应的值类型;
※当类型参数被指定为引用类型时,会在第一次指定任意引用类型时创建一个通用化泛型类型,泛型类型中的类型参数会被替换为该引用类型,并在之后每次指定为引用类型时重用该泛型类型并修改其中类型参数的类型;造成这种差异的原因可能在于所有的引用大小相同;
二、这篇我们先了解下泛型类,泛型类的定义中可以将类型参数用作成员变量的类型或方法中参数列表、返回值的类型:
class MyClass<T> //声明具有一个类型参数的泛型类,可以有多个类型参数,用,隔开:<T, U>{ public T MyObj; //声明T类型的字段 private Type myGenericField = typeof(T); //在泛型类内部可以获取类型参数的类型信息 //do…}//声明泛型类的实例,指定类型参数为int类型MyClass<int> myObj = new MyClass<int>();
1.在定义泛型类型时,可以对类型参数的种类添加限制,这些限制称为约束(Constraint),使用约束可以增加类型参数所能进行操作和调用方法的数量;约束使用上下文关键字where指定,位于基类和接口之后,例如:
class MyClass<T> : MyBaseClass where T : MyType //指定基类约束,T需要是指定的类MyType或继承自类MyType,基类约束需要在所有约束之前,基类约束本身也可以是泛型类型,例如MyType<T>
※其它特殊的约束:
where T : IMyInterface //指定接口约束,T需要是指定的接口IMyInterface或实现接口IMyInterface,可以同时指定多个接口约束,接口约束本身也可以是泛型类型,例如IMyInterface<T>
where T : class //指定类型约束,T需要是类类型
where T : struct //指定类型约束,T需要是值类型,但不可以是可空类型
where T : new() //类型参数必须有公共的无参数构造函数,与其他约束一起使用时,new()约束必须最后指定;由于结构的定义中一定包含无参数构造函数,所以struct约束包含new()约束,二者不可同时使用,通常与class约束一起使用:class, new()
where T : struct where U : class //给多个类型参数指定约束
where U : T //类型参数作为约束,类型参数U继承自类型参数T
※可以对一个类型参数应用多个约束,多个约束使用,隔开:where T : MyType, IMyInterface;
※没有约束的类型参数称为未绑定类型参数(Unbounded Type Parameter),这些类型参数的变量在使用时不可以使用==和!=运算符,因为无法保证运行时指定的类型支持这些运算符;
※对于使用类型约束class的类型参数,应避免对其变量使用==和!=运算符,因为在泛型中这些运算符仅会根据引用来判断是否相等,即使类型对==和!=运算符进行了重载也不行;如果必须根据值进行判断,应给类型参数加入基类约束IEquatable<T>或IComparable<T>并在类型中实现它们,比较时使用Equals或CompareTo;
※从C#7.3开始,可以使用特殊类型System.Delegate、System.MulticastDelegate和System.Enum作为基类约束中的基类,还可以使用非托管类型unmanaged作为类型约束中的类型;
2.非泛型类(即具体类,Concrete Class)只可以继承自具体类或封闭式构造类(即指定了所有类型参数的泛型类,Closed Constructed Class),不可以继承自开放式构造类(即没有完全指定所有类型参数的泛型类,Open Constructed Class);泛型类可以继承自具体类和封闭式构造类,也可以继承自开放式构造类,继承自开放式构造类时,派生类的类型参数中必须包含基类中未指定类型的类型参数,同时,派生类中这些类型参数的约束必须为基类中对应类型参数约束的超集;可以总结为以下几种情况:
//对于基类为具体类或仅有一个类型参数的情况class BaseClass { }class BaseGenericClass<T> { }//定义一个泛型类,继承具体类BaseClassclass MyClass<T> : BaseClass { }//定义一个泛型类,继承自指封闭式构造类BaseGenericClass<int>class MyClass<T> : BaseGenericClass<int> { }//定义一个泛型类,继承自开放式构造类BaseGenericClass<T> class MyClass<T> : BaseGenericClass<T> { }//定义一个非泛型类,继承自封闭式构造类BaseGenericClass<int>class MyClass : BaseGenericClass<int> { }//对于基类有多个类型参数的情况class MultipleBaseGenericClass<T, U> { }//定义一个泛型类,继承自开放式构造类MultipleBaseGenericClass<T, int>class MyClass<T> : MultipleBaseGenericClass<T, int> { }//定义一个泛型类,继承自开放式构造类MultipleBaseGenericClass<T, U>class MyClass<T, U> : MultipleBaseGenericClass<T, U> { }//定义一个非泛型类,继承自封闭式构造类MultipleBaseGenericClass<int, string>class MyClass : MultipleBaseGenericClass<int, string> { }
※在继承中,派生类类型的对象可以通过隐式转换赋值给基类类型的变量,这同样适用于泛型类型的对象,例如泛型类List<T>继承自泛型接口IList<T>,那么可以把List<int>类型的对象隐式转换为IList<int>类型的变量:
IList<int> iList = new List<int>();
3.泛型类为不可变量,泛型类型相同但类型参数指定类型不同的泛型类型即是不同的类型,它们之间不能进行类型转换(即使类型参数指定的类型之间存在类型转换关系),例如不能把List<DerivedClass>类型的对象赋值给一个List<BaseClass>类型的变量;
4.泛型类最常见的用途是创建泛型集合类,在命名空间System.Collections.Generic中包含系统定义的各种泛型集合类,应尽可能的使用这些泛型集合来代替普通的集合;
原文作者:Minotauros
原文地址:https://www.cnblogs.com/minotauros/p/9930163.html