在C#中,泛型(Generics)是一种允许你定义灵活的类、结构、接口和方法的方式,这些类、结构、接口和方法可以处理任意数据类型,而不需要在运行时确定具体的类型。泛型的主要优点是类型安全和代码重用。
以下是C#泛型的一些基本特性和使用方式的解析:
1. 泛型类
泛型类允许你定义一个类,该类可以操作任意类型的数据,但具体类型在创建类的实例时确定。
csharp复制代码
public class GenericList<T> | |
{ | |
private T[] items; | |
private int count; | |
private int capacity; | |
// 构造函数和其他方法... | |
public T this[int index] | |
{ | |
get { return items[index]; } | |
set { items[index] = value; } | |
} | |
// ...其他成员... | |
} | |
// 使用时: | |
GenericList<int> intList = new GenericList<int>(); | |
GenericList<string> stringList = new GenericList<string>(); |
2. 泛型接口
泛型接口允许你定义一个接口,该接口可以包含使用类型参数的方法、属性、索引器或事件。
csharp复制代码
public interface IGenericRepository<T> | |
{ | |
T GetById(int id); | |
void Add(T item); | |
// ...其他成员... | |
} | |
// 实现时: | |
public class ProductRepository : IGenericRepository<Product> | |
{ | |
// 实现IGenericRepository<Product>接口的成员 | |
} |
3. 泛型方法
泛型方法允许你在方法签名中声明类型参数。
csharp复制代码
public static T Find<T>(T[] items, Predicate<T> match) | |
{ | |
foreach (T item in items) | |
{ | |
if (match(item)) | |
{ | |
return item; | |
} | |
} | |
return default(T); // 如果没有找到匹配的项,则返回类型的默认值 | |
} | |
// 使用时: | |
int[] numbers = { 1, 2, 3, 4, 5 }; | |
int result = Find(numbers, n => n > 3); // 返回4 |
4. 泛型约束
你可以对泛型类型参数应用约束,以限制可以用作类型参数的类型。约束使用 where
关键字定义。
csharp复制代码
public class BaseClass { } | |
public class GenericClass<T> where T : BaseClass // 约束T必须是BaseClass或其派生类 | |
{ | |
// ...类成员... | |
} | |
// 另一个示例,约束类型参数必须是引用类型或值类型(即任何类型) | |
public class AnotherGenericClass<T> where T : class // 约束T必须是引用类型 | |
{ | |
// ...类成员... | |
} | |
public class YetAnotherGenericClass<T> where T : struct // 约束T必须是值类型 | |
{ | |
// ...类成员... | |
} |
5. 泛型委托和泛型事件
C# 中的委托和事件也可以使用泛型。例如,Predicate<T>
和 Func<TResult>
是内置的泛型委托。
6. 泛型集合
.NET 框架提供了许多泛型集合类,如 List<T>
、Dictionary<TKey, TValue>
、HashSet<T>
等,这些类提供了类型安全的集合操作。
7. 泛型协变和逆变
在C# 4.0及更高版本中,引入了协变(covariance)和逆变(contravariance)的概念,允许在泛型接口和委托的引用类型参数上进行更灵活的转换。这通过 in
和 out
关键字在泛型接口和委托的声明中指定。
8. 泛型的好处
- 类型安全:在编译时检查类型,减少运行时错误。
- 代码重用:通过编写一次泛型代码,可以处理多种数据类型。
- 性能:通常比使用
object
或非泛型集合进行装箱和拆箱操作更高效。 - 可读性:泛型代码通常更清晰,因为它们明确地指定了它们可以处理的数据类型。