在 C# 中,泛型(Generics) 是一种强大的编程特性,允许你编写可重用、类型安全的代码,而无需为不同类型重复编写相似的逻辑。泛型的核心思想是参数化类型,即通过占位符(如 T)表示类型,在编译时确定具体类型。以下是泛型的详细讲解:
1. 泛型的基本概念
- 类型参数化:用占位符(如 T、TKey、TValue)代替具体类型。
- 编译时类型安全:泛型在编译时检查类型一致性,避免运行时类型错误。
- 避免装箱拆箱:泛型可避免值类型和引用类型之间的转换(如 List 比 ArrayList 更高效)。
2. 泛型的实现方式
(1) 泛型类
public class GenericClass<T>
{
private T _value;
public void SetValue(T value)
{
_value = value;
}
public T GetValue()
{
return _value;
}
}
// 使用示例
var intClass = new GenericClass<int>();
intClass.SetValue(42);
int value = intClass.GetValue(); // 类型安全,直接返回 int
(2) 泛型方法
public class Utility
{
public static T Max<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) > 0 ? a : b;
}
}
// 使用示例
int maxInt = Utility.Max(3, 5); // 返回 5
string maxStr = Utility.Max("A", "B"); // 返回 "B"
(3) 泛型接口
public interface IRepository<T>
{
void Add(T entity);
T GetById(int id);
}
public class UserRepository : IRepository<User>
{
public void Add(User user) { /* 实现 */ }
public User GetById(int id) { /* 实现 */ }
}
3. 泛型约束(Constraints)
通过 where 关键字限制泛型类型参数的范围,增强类型安全性和功能可用性。
约束类型 | 语法 | 说明 |
---|---|---|
类约束 | where T : class | T 必须是引用类型 |
结构约束 | where T : struct | T 必须是值类型 |
构造函数约束 | where T : new() | T 必须有无参构造函数 |
基类约束 | where T : BaseClass | T 必须继承自 BaseClass |
接口约束 | where T : IInterface | T 必须实现 IInterface |
组合约束 | where T : A, I, new() | 可组合多个约束 |
示例:
public class Factory<T> where T : IEntity, new() // IEntity代表某个实体类
{
public T Create()
{
var entity = new T();
entity.Initialize();
return entity;
}
}
4. 泛型的常见应用场景
(1) 集合类(如 List、Dictionary<TKey, TValue>)
泛型集合取代了非泛型集合(如 ArrayList),避免了类型转换和装箱拆箱:
List<int> numbers = new List<int> { 1, 2, 3 };
Dictionary<string, int> ages = new Dictionary<string, int> { { "Alice", 30 } };
(2) 泛型委托(如 Action、Func)
// 定义泛型委托
public delegate void Logger<T>(T message);
// 使用示例
Logger<string> logString = message => Console.WriteLine(message);
Logger<int> logInt = number => Console.WriteLine($"Number: {number}");
(3) 泛型单例模式
public class Singleton<T> where T : new()
{
private static readonly Lazy<T> _instance = new Lazy<T>(() => new T());
public static T Instance => _instance.Value;
}
// 使用示例
public class AppConfig { /* 配置类 */ }
var config = Singleton<AppConfig>.Instance;
5. 泛型的特殊特性
(1) 默认值(default 关键字)
T value = default(T); // 对于引用类型返回 null,值类型返回 0 等
(2) 协变(Covariance)和逆变(Contravariance)
- 协变(out 关键字):允许子类型替换父类型(如 IEnumerable)。
- 逆变(in 关键字):允许父类型替换子类型(如 Action)。
示例:
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // 协变允许隐式转换
Action<object> logObject = obj => Console.WriteLine(obj);
Action<string> logString = logObject; // 逆变允许隐式转换
6. 泛型与反射
通过反射可以获取泛型类型的元数据:
Type listType = typeof(List<>);
Type intListType = listType.MakeGenericType(typeof(int));
var intList = Activator.CreateInstance(intListType);
7. 注意事项
- 避免过度泛型化:只在需要类型灵活性的场景使用泛型。
- 命名规范:类型参数名应具有描述性(如 TKey、TValue)。
- 性能优化:泛型在编译时生成具体类型代码,不会带来运行时性能损失。
8. 典型问题与解决
问题:无法直接比较泛型类型
public bool AreEqual<T>(T a, T b)
{
// 错误:T 可能未实现比较接口
return a == b;
}
// 解决方案:添加 IEquatable<T> 约束
public bool AreEqual<T>(T a, T b) where T : IEquatable<T>
{
return a.Equals(b);
}
通过泛型,C# 可以在编译时保证类型安全,同时提高代码的灵活性和复用性。合理使用泛型,可以显著减少重复代码并提升程序的可维护性。