C# Generic 泛型
什么是泛型(Generic)?
很多编程语言当中都有泛型这个概念,泛型是一种语言规范,是一种允许延迟编写类或方法中编程元素的数据类型的规范,换句话说,泛型允许我们编写一个可以与任何数据类型一起工作的类或方法,用一个方法或类,满足不同的参数做相同的事情。
在C#中,泛型可以被应用在,方法、类、接口和委托上,能够很好的帮助我们复用代码,减少工作量。接下来我们来看一段泛型方法的实现代码。
1
2
3
4
5
6
7
8
9
static void Main(string[] args)
{
int data = 10;
GenericMethod(data);
}
public static void GenericMethod<T>(T data)
{
Console.WriteLine($"{data} is {typeof(T)}");
}
在上述代码中我们实现了一个静态泛型方法,可以看到和普通静态方法不同的是,泛型静态方法的方法名后面跟着一个“
可以看到,我们确实是成功传了一个int类型的数据进去。现在我们就可以来试试传各种类型的数据试试。
可以看到,我们成功用一个方法就实现了对不同数据类型的操作,这就是泛型的作用。当然,泛型的作用还远不止于此,除了泛型方法之外,我们还可以写泛型类等等,同时我们也可以有多个类型占位符,类型占位符不仅可以用在规定传入的参数类型上,同样的也可以规定返回值的类型,下面这个简单的泛型类就展示了这些。
上述代码就实现了一个拥有两个参数类型占位符的泛型类。
C#是如何实现泛型的呢?更准确的来说,.net框架是如何实现泛型的呢?这个时候就要引入一个概念就是“延迟声明”,延迟声明就是在调用时才声明参数的类型,vs将我们的代码编译为IL(Intermediate Language)中间语言,在我们点击exe文件运行程序时,运行在.net框架上的IL就会被转译为机器语言,并且在这个过程中,.net框架的JIT就会替换占位符为正确的类型。大致上泛型的实现就可以这么理解,总之,泛型很好用,性能开销也和普通的写死参数类型的方法和类差别不大,所以我们有复用代码的需求都可以使用泛型来实现。
泛型约束
泛型约束的出现是为了解决一种情况的,就是当我们要在一个泛型方法或者泛型类中访问传进来的泛型类型所指代的类的属性或者方法时,编译器并不知道这个传进来的泛型类型的类到底是哪个类,所以也就不知道有哪些属性和方法,这个时候编辑器就会报错。接下来我们复现一下这个错误。
上述代码就很好的说明了这种问题,当我们想写一个专门为某一个及其子类的服务的方法的时候,我们难免会在方法中去访问类中的方法或者属性,这个时候问题就出现了,我们这时就可以使用泛型约束来规定当前的这个类型占位符必须是那种类或及其子类的。
我们在泛型方法声明后加上 where T:Baseclass,这句话的意思就是,我要将T的类型范围约束到BassClass及其子类,为什么子类也可以呢,这很好理解,因为父类有的东西子类也会有。泛型约束给我们提供了便利,让我们能够访问我们在运行时才能访问到的属性和变量,便利的同时我们也有义务在传递参数时只能传递我们约束指定的类及其子类。
俗话说无约束,不自由。我们在设计代码的时候就要考虑到我们提供的方法需要传递哪些类型,不能把一切都抛给编译器去完成,不然编译器迟早得罢工。
泛型约束有很多种,上述的泛型约束名为基类约束,还有引用类型约束、值类型约束、无参数构造函数约束等等约束,除了泛型约束之外,关于约束的使用技巧还有协变、逆变、泛型缓存等等。
具体的实现效果和应用场景请看泛型约束、协变、逆变、泛型缓存




