C# 浅析泛型

C# 中的泛型(Generics)是一种非常强大的功能,它允许你定义类、接口、方法和委托时不指定特定的数据类型,而是使用类型参数来代替。在实例化或调用时,才指定实际的类型。这样可以提高代码的复用性、类型安全性和性能。

为什么使用泛型?

  1. 类型安全:泛型允许你在编译时就检查类型,而不是在运行时,这有助于避免类型转换错误。
  2. 代码复用:你可以编写一次代码,并针对不同的数据类型进行重用。
  3. 性能优化:避免了装箱和拆箱操作,从而提升了性能(尤其在处理值类型时)。

泛型的基础概念

泛型类 (Generic Classes)

泛型类允许你创建一个类,其中的字段、方法和属性可以处理任何类型的数据,而不需要为每个类型分别写一个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class GenericClass<T>
{
private T _value;

public GenericClass(T value)
{
_value = value;
}

public T GetValue()
{
return _value;
}

public void SetValue(T value)
{
_value = value;
}
}

在上述代码中,GenericClass<T> 是一个泛型类,其中 T 是一个类型参数,它可以是任何类型。在使用时,可以这样实例化:

1
2
3
4
5
GenericClass<int> intInstance = new GenericClass<int>(10);
int value = intInstance.GetValue(); // value 为 10

GenericClass<string> stringInstance = new GenericClass<string>("Hello");
string str = stringInstance.GetValue(); // str 为 "Hello"

泛型方法 (Generic Methods)

泛型方法允许你在方法级别定义类型参数,这使得方法可以独立于包含它的类成为泛型。

1
2
3
4
5
6
7
public class Utility
{
public static void PrintType<T>(T item)
{
Console.WriteLine($"Type of item: {typeof(T)}, Value: {item}");
}
}

你可以这样调用泛型方法:

1
2
3
IRepository<string> stringRepository = new Repository<string>();
stringRepository.Add("Hello");
Console.WriteLine(stringRepository.Get(0)); // 输出:Hello

泛型接口 (Generic Interfaces)

泛型接口允许你定义一种通用的行为模式,可以用于不同类型的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
csharp
Copy code
public interface IRepository<T{void Add(T item);T Get(int id);
}

public class Repository<T: IRepository<T{private List<T> _items = new List<T>();
public void Add(T item)
{
_items.Add(item);
}
public T Get(int id)
{return _items[id];
}
}

你可以这样使用泛型接口和类:

1
2
3
IRepository<string> stringRepository = new Repository<string>();
stringRepository.Add("Hello");
Console.WriteLine(stringRepository.Get(0)); // 输出:Hello

泛型约束 (Generic Constraints)

C# 允许你对泛型类型参数添加约束,以限制可以用于泛型的类型。常见的约束有:

  1. where T : class:T 必须是一个引用类型。
  2. where T : struct:T 必须是一个值类型。
  3. **where T : new()**:T 必须有一个无参数的构造函数。
  4. **where T : **:T 必须继承指定的基类。
  5. **where T : **:T 必须实现指定的接口。
1
2
3
4
5
6
7
public class GenericWithConstraint<T> where T : class, new()
{
public T CreateInstance()
{
return new T();
}
}

在这个例子中,类型参数 T 必须是一个引用类型,并且具有一个无参数的构造函数。

常见的泛型集合类

C# 提供了一系列常见的泛型集合类,这些类大大提高了开发效率和代码的类型安全性:

  • List: 动态数组,可以存储任何类型的元素。
  • Dictionary<TKey, TValue>: 键值对集合,用于存储成对的数据。
  • Queue: 先进先出的集合。
  • Stack: 后进先出的集合。
1
2
3
4
5
6
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};

泛型的高级用法

协变与逆变

C# 支持协变(Covariance)和逆变(Contravariance),使得泛型类型参数可以在继承层次结构中进行转换。协变和逆变主要应用于泛型接口和委托。

1
2
3
4
5
6
7
// 协变允许你将一个泛型接口的派生类型转换为基类型
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // 协变

// 逆变允许你将一个泛型接口的基类型转换为派生类型
Action<object> objectAction = obj => Console.WriteLine(obj);
Action<string> stringAction = objectAction; // 逆变

总结

泛型是 C#中的一个核心概念,通过提供类型安全的代码复用能力,大大提高了代码的灵活性和性能。在实际开发中,合理使用泛型可以让你的代码更加简洁、易读和高效。无论是定义自己的泛型类和方法,还是利用 C#提供的泛型集合类,掌握泛型的使用将是提升你编程能力的重要一步。