C# 泛型
泛型(Generics)是类型参数化的机制,允许你在编写类、结构、接口、方法和委托时使用占位符表示的类型。这样可以提高代码的重用性、类型安全性和性能。这种特性广泛应用于类型安全的集合类、算法和数据结构中。
类型参数:泛型中的类型参数是一个占位符,它在类、接口或方法的定义中表示一种未确定的类型。在使用泛型时,可以将具体的类型传递给这些占位符。通常使用 T
、U
、V
等字母来表示类型参数。(大多数使用T
这个字母是因为Type
缩写)
类型安全:泛型提供了类型安全性,编译器会在编译时检查类型一致性,从而避免了许多运行时错误。泛型使得在不需要显式类型转换的情况下,可以灵活地处理不同类型的数据。
代码复用:泛型可以极大地提高代码的复用性,因为相同的泛型类或方法可以适用于不同的数据类型,而不需要为每一种类型单独编写代码。
性能提升:泛型避免了装箱和拆箱,减少了运行时的开销,从而提高了性能。
简化维护:由于泛型可以减少代码的重复性,使得代码更加简洁易读,减少了维护的难度。
基本语法
泛型类
1 | public class GenericClass<T> |
泛型方法
1 | public class GenericMethodClass |
泛型接口
1 | public interface IGenericInterface<T> |
泛型委托
1 | public delegate T GenericDelegate<T>(T item); |
C# 泛型运行原理
C# 程序在编译时,由编译器将源代码编译成中间语言(IL)代码,并打包成 .exe
或 .dll
文件。对于泛型类型和泛型方法,编译器生成的是一个泛型类型定义(Generic Type Definition),该定义中包含了未具体化的泛型类型参数。
在运行时,当 .NET 运行时中的 JIT 编译器首次遇到一个泛型类型的具体实例化(例如 List<int>
),它会基于泛型类型定义生成针对该类型的机器代码。对于值类型,JIT 会为每个不同的值类型生成单独的代码;对于引用类型,JIT 会重用相同的代码。
这种双重编译机制(C# 编译器的编译和 JIT 编译器的即时编译)确保了泛型在运行时的类型安全性和高效性。
泛型约束
可以使用泛型约束来限制泛型类型参数必须满足的条件,例如必须实现某个接口或必须有一个无参数的构造函数。
1 | public class GenericClassWithConstraint<T> where T : IComparable, new() |
泛型限定条件:
- T:struct(类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型)
- T:class(类型参数必须是引用类型,包括任何类、接口、委托或数组类型)
- T:new() (类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时new() 约束必须最后指定)
- T:<基类名> 类型参数必须是指定的基类或派生自指定的基类
- T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
协变与逆变
协变(Covariance)和逆变(Contravariance)是编程语言中处理泛型、委托和接口时的一种重要概念,它们决定了如何在继承关系中处理类型的转换。协变和逆变帮助我们在泛型类型中灵活地处理类型参数,使代码更具通用性和重用性。
协变
方向:从子类到父类。
应用场景:主要用于返回值类型的转换。
协变允许你在类型转换时,将一个 泛型类型的派生类型 赋值给 其基类型的泛型类型。协变主要应用于返回类型,即你可以将派生类型的泛型对象赋给基类型的泛型对象。
通过给泛型接口的 类型参数 前添加out
关键字来表示T
是协变的。(类型参数:即尖括号内的T
)
1 | public class Animal{} |
1 | IConvariant<Cat> cat = null; |
通过上面的例子,我们可以发现,ICovariant<Car>
可以赋值给 ICovariant<Animal>
,因为 Cat
是 Animal
的子类。
协变限制
当类型参数被标记为协变时,该类型参数只能用于返回值,不能用作方法的输入参数。这是因为协变的方向是从子类到父类,而允许将 T
用作输入参数可能会破坏类型安全。
逆变
方向:从父类到子类。
应用场景:主要用于参数类型的转换。
逆变允许你在类型转换时,将一个 **泛型类型的基类型 **赋值给 其派生类型的泛型类型。逆变主要应用于参数类型,即你可以将基类型的泛型对象赋给派生类型的泛型对象。
1 | public class Animal{} |
1 | IConvariant<Animal> animal = null; |
通过上面的例子,我们可以发现,IContravariant<Animal>
可以赋值给 IContravariant<Dog>
,因为 Animal
是 Dog
的基类。
逆变限制
当类型参数被标记为逆变时,该类型参数只能用作方法的输入参数,不能作为返回值使用。这是因为逆变的方向是从基类到子类,而允许将 T
用作返回值可能会破坏类型安全。
- 标题: C# 泛型
- 作者: 日之朝矣
- 创建于 : 2024-08-10 11:05:14
- 更新于 : 2024-08-18 09:25:27
- 链接: https://blog.rzzy.fun/2024/08/10/csharp-generic-type/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。