C# 中的泛型(Generics)是一种非常强大的功能,它允许你定义类、接口、方法和委托时不指定特定的数据类型,而是使用类型参数来代替。在实例化或调用时,才指定实际的类型。这样可以提高代码的复用性、类型安全性和性能。
为什么使用泛型?
- 类型安全:泛型允许你在编译时就检查类型,而不是在运行时,这有助于避免类型转换错误。
- 代码复用:你可以编写一次代码,并针对不同的数据类型进行重用。
- 性能优化:避免了装箱和拆箱操作,从而提升了性能(尤其在处理值类型时)。
泛型的基础概念
泛型类 (Generic Classes)
泛型类允许你创建一个类,其中的字段、方法和属性可以处理任何类型的数据,而不需要为每个类型分别写一个类。
1 | public class GenericClass<T> |
在上述代码中,GenericClass<T>
是一个泛型类,其中 T
是一个类型参数,它可以是任何类型。在使用时,可以这样实例化:
1 | GenericClass<int> intInstance = new GenericClass<int>(10); |
泛型方法 (Generic Methods)
泛型方法允许你在方法级别定义类型参数,这使得方法可以独立于包含它的类成为泛型。
1 | public class Utility |
你可以这样调用泛型方法:
1 | IRepository<string> stringRepository = new Repository<string>(); |
泛型接口 (Generic Interfaces)
泛型接口允许你定义一种通用的行为模式,可以用于不同类型的数据。
1 | csharp |
你可以这样使用泛型接口和类:
1 | IRepository<string> stringRepository = new Repository<string>(); |
泛型约束 (Generic Constraints)
C# 允许你对泛型类型参数添加约束,以限制可以用于泛型的类型。常见的约束有:
- where T : class:T 必须是一个引用类型。
- where T : struct:T 必须是一个值类型。
- **where T : new()**:T 必须有一个无参数的构造函数。
- **where T :
**:T 必须继承指定的基类。 - **where T :
**:T 必须实现指定的接口。
1 | public class GenericWithConstraint<T> where T : class, new() |
在这个例子中,类型参数 T
必须是一个引用类型,并且具有一个无参数的构造函数。
常见的泛型集合类
C# 提供了一系列常见的泛型集合类,这些类大大提高了开发效率和代码的类型安全性:
- List
: 动态数组,可以存储任何类型的元素。 - Dictionary<TKey, TValue>: 键值对集合,用于存储成对的数据。
- Queue
: 先进先出的集合。 - Stack
: 后进先出的集合。
1 | List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; |
泛型的高级用法
协变与逆变
C# 支持协变(Covariance)和逆变(Contravariance),使得泛型类型参数可以在继承层次结构中进行转换。协变和逆变主要应用于泛型接口和委托。
1 | // 协变允许你将一个泛型接口的派生类型转换为基类型 |
总结
泛型是 C#中的一个核心概念,通过提供类型安全的代码复用能力,大大提高了代码的灵活性和性能。在实际开发中,合理使用泛型可以让你的代码更加简洁、易读和高效。无论是定义自己的泛型类和方法,还是利用 C#提供的泛型集合类,掌握泛型的使用将是提升你编程能力的重要一步。