在现代 C# 开发中,**泛型(Generic)**是提升代码复用性、类型安全和性能的重要工具。通过泛型,你可以编写与具体数据类型无关的类、方法、委托或接口,从而让程序更加灵活和高效。本文将从基础概念、语法、实例到高级约束与最佳实践,系统讲解泛型在 C# 中的应用。
一、什么是泛型
泛型的核心思想是:延迟指定数据类型。传统类或方法在编写时就固定了数据类型,而泛型允许你在使用时再指定类型。例如,你可以写一个"通用数组类",既可以存储整数,也可以存储字符串或自定义类型,而不需要为每种类型都写一个新类。
泛型的优势:
- 类型安全:编译器会在编译阶段检查类型,减少运行时错误。
- 提高复用性:一份代码可适配多种类型。
- 性能优化:避免值类型装箱与拆箱,提高运行效率。
- 简化 API:与非泛型集合相比,代码更简洁直观。
二、泛型类
泛型类是最常见的泛型形式。下面用一个自定义泛型数组类来演示:
csharp
using System;
namespace GenericDemo
{
public class MyGenericArray<T>
{
private T[] array;
public MyGenericArray(int size) => array = new T[size];
public void SetItem(int index, T value) => array[index] = value;
public T GetItem(int index) => array[index];
}
class Program
{
static void Main()
{
// 整型数组
var intArray = new MyGenericArray<int>(5);
for (int i = 0; i < 5; i++) intArray.SetItem(i, i * 10);
for (int i = 0; i < 5; i++) Console.Write(intArray.GetItem(i) + " ");
Console.WriteLine();
// 字符数组
var charArray = new MyGenericArray<char>(5);
for (int i = 0; i < 5; i++) charArray.SetItem(i, (char)('A' + i));
for (int i = 0; i < 5; i++) Console.Write(charArray.GetItem(i) + " ");
}
}
}
输出结果:
0 10 20 30 40
A B C D E
分析:
T是类型参数,占位符类型。- 在创建对象时,
int和char被替换到T,编译器会生成对应类型的具体实现。 - 泛型保证了类型安全,避免了手动类型转换。
三、泛型方法
泛型方法的类型参数只对该方法有效,而不依赖于类的泛型参数。典型例子是交换两个变量的值:
csharp
using System;
class GenericMethods
{
static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
static void Main()
{
int x = 5, y = 10;
char c1 = 'X', c2 = 'Y';
Swap<int>(ref x, ref y);
Swap<char>(ref c1, ref c2);
Console.WriteLine($"x={x}, y={y}");
Console.WriteLine($"c1={c1}, c2={c2}");
}
}
输出结果:
x=10, y=5
c1=Y, c2=X
泛型方法提高了函数的灵活性,无论整数、字符甚至自定义类都可以直接使用。
四、泛型委托
泛型不仅适用于类和方法,也可以用于委托,实现类型安全的回调机制。例如:
csharp
using System;
delegate T Operation<T>(T value);
class GenericDelegateDemo
{
static int AddTen(int x) => x + 10;
static int MultiplyByTwo(int x) => x * 2;
static void Main()
{
Operation<int> add = AddTen;
Operation<int> multiply = MultiplyByTwo;
Console.WriteLine(add(5)); // 输出 15
Console.WriteLine(multiply(5)); // 输出 10
}
}
泛型委托可适配多种类型的操作,并确保类型安全,避免了非泛型委托可能出现的类型转换异常。
五、泛型约束
在实际开发中,有时需要限制泛型类型以满足特定条件,例如只能是引用类型或值类型。C# 提供了丰富的约束选项:
| 约束 | 说明 |
|---|---|
where T : struct |
必须是值类型(非 Nullable) |
where T : class |
必须是引用类型 |
where T : new() |
必须有无参数构造函数 |
where T : BaseClass |
必须继承自指定类 |
where T : IInterface |
必须实现指定接口,可组合多个接口 |
示例:
csharp
public class Factory<T> where T : new()
{
public static T Create() => new T();
}
class Person { public string Name; }
class Program
{
static void Main()
{
var person = Factory<Person>.Create();
person.Name = "张三";
Console.WriteLine(person.Name);
}
}
分析:
new()约束保证T可通过new T()实例化。- 泛型约束可以组合,例如
where T : class, IComparable, new()。
六、泛型的应用场景
泛型在以下场景最常用:
- 集合类 :如
List<T>、Dictionary<TKey,TValue>。 - 缓存管理 :如
CacheHelper<T>类可以存取任意类型的缓存对象。 - 通用工具类:排序、查找、比较等算法。
- 事件与回调:泛型委托提高事件处理的灵活性和类型安全。
七、泛型的性能与注意事项
- 泛型在编译期生成具体类型代码,不会增加运行时开销。
- 使用值类型泛型可避免装箱操作,性能优于非泛型集合。
- 泛型约束有助于提供编译期检查,减少运行时错误。
- 注意不要过度泛型化,复杂的泛型嵌套会降低可读性。
八、总结
C# 泛型是提升代码复用性、类型安全和性能的重要工具。掌握泛型类、泛型方法、泛型委托以及约束,你可以编写更加灵活、健壮、高效的程序。
核心要点:
- 泛型通过类型参数延迟指定类型。
- 泛型方法和泛型委托提升函数和回调的灵活性。
- 泛型约束保证类型安全,便于公共组件开发。
- 应用泛型集合可以替代传统非泛型集合,避免类型转换问题。
泛型是现代 C# 开发的基础技能,理解并熟练使用它,可以让你的程序更优雅、更专业。