本节内容为泛型
为什么需要泛型泛型是一个非常有趣的东西,他的出现对于减少代码复用率有了很大的帮助。比如说遇到两个模块的功能非常相似,只是一个是处理int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗?
泛型简介在我们的C#中,使用泛型对允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。泛型的定义非常简单,在类或函数名后使用作为占位符即可,这个T也可以换成其他的字母代替。
注意:属性和索引器不能指定自己的泛型参数,它们只能使用所属类中定义的泛型参数进行操作。
你可以通过下面这个例子得到一些关于泛型定义的方法。
值得注意的是泛型是在运行时进行动态变化,并不是在编译时发生。
泛型类与泛型函数泛型类和泛型函数在使用上基本上是一样的,只不过定义后的范围不一样。对于泛型类,泛型的范围是整个类,泛型函数则是在函数内部。
例如这个例子
class A<T> { public T getSomething<X>(X m,T n) { return n; } public static U test<U>(U x) { return x; } } // 实例泛型类必须指定类型 A<string> a = new A<string>() //泛型推断 A.test<int>(1);//原式 A.test(1);//推断在泛型函数的调用中,有一个语法糖,它就是泛型类型推断。这非常好理解,C#的编译器足够聪明,它可以根据你传入的参数类型,调用gettype方法进行类型的推断。因此你可以在泛型函数中不显式的指定类型。
类型推理的相同规则适用于静态方法和实例方法。 编译器可基于传入的方法参数推断类型参数;而无法仅根据约束或返回值推断类型参数。 因此,类型推理不适用于不具有参数的方法。 类型推理发生在编译时,之后编译器尝试解析重载的方法签名。 编译器将类型推理逻辑应用于共用同一名称的所有泛型方法。 在重载解决方案步骤中,编译器仅包含在其上类型推理成功的泛型方法。
泛型的范围则是包含关系。包含在泛型类中的泛型函数可以自由的访问泛型类中的泛型,但是类不可以访问泛型函数中指定的泛型。
泛型约束如果我们使用了泛型,那么必定面临的一个问题就是权限问题。例如class A,假定我希望某些类型不可以作为泛型传入,那么我们就应当使用我们的泛型约束。
泛型约束的使用如下例:
泛型约束通常有下面几类:
where T : struct:类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。
where T : class 类型参数必须是引用类型。 此约束还应用于任何类、接口、委托或数组类型。
where T : unmanaged 类型参数不能是引用类型,并且任何嵌套级别均不能包含任何引用类型成员。
where T : new() 类型参数必须具有公共无参数构造函数。 与其他约束一起使用时,new() 约束必须最后指定。
where T : 类型参数必须是指定的基类或派生自指定的基类。
where T : 类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。
where T : U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。
某些约束是互斥的。 所有值类型必须具有可访问的无参数构造函数。 struct 约束包含 new() 约束,且 new() 约束不能与 struct 约束结合使用。 unmanaged 约束包含 struct 约束。 unmanaged 约束不能与 struct 或 new() 约束结合使用。使用的时候稍加注意即可。
你也可以指定多个类型占位符,并且单独为他们进行约束,如:
class A<T,U> where T:struct where U:class甚至你可以进行泛型自我约束,例如:
class A<T,U,K> where T:struct where U:K 协变和逆变这三个名词来自于数学和物理,很多初学者都难以理解这些名词。但事实上在C#上,这些词是用于标示类型与类型之间的绑定。可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变。
协变如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的。直白的说,协变就是合理的变化,例如猫->动物,这个看上去丝毫没有问题。这就是协变,从小变大。