泛型
泛型不是语法糖,而是由框架提供的一种便捷语法,首次出现在.NET 2.0中。
1. 泛型定义
- 泛型:是一种程序特性,定义时不对类型做出明确的规定,使用时规定且不能改变。
- 一般应用:泛型集合、泛型方法、泛型类、泛型委托。
- 泛型方法:方法的返回值类型、参数类型定义成泛型类型。
- 泛型集合:如
List<T>
、Dictionary<K, V>
,所在命名空间:System.Collections.Generic
。 - 非泛型集合:
ArrayList
、Hashtable
,所在命名空间:System.Collections
。非泛型集合中可以添加任意类型,但对于数据本身来说,这是非常不安全的,并且存在装箱和拆箱问题。- 装箱:将值类型的元素放到集合中会被转换成
object
类型。 - 拆箱:将一个集合的元素取出来,但这个元素本身是值类型,必须进行强制类型转换。
- 弊端:如果存储大量的数据,会影响性能。
- 装箱:将值类型的元素放到集合中会被转换成
2. 泛型赋值与约束
- default关键字:主要用于直接的赋值,引用类型赋值为
null
,值类型给值默认值,如T a = default(T)
。 - 泛型约束:
where T1 : class
//T1
必须是引用类型。where T2 : struct
//T2
必须是值类型。where T3 : new()
// 必须有一个无参构造方法。where T4 : 接口
// 接口约束。where T5 : 类名
// 基类约束。
- 约束是可以叠加的,比单个基类更灵活的约束目标。
- 由于泛型类型是不确定的,所以泛型类型在编译阶段无法直接转换。可采用
dynamic
类,在运行动态解析对象类型。在编译阶段是不解析的,类型转换不对,会在运行时报错。
代码示例
1. 代码重用
public class GenericList<T>
{private T[] items;private int count;public GenericList(int capacity){items = new T[capacity];}public void Add(T item){if (count == items.Length){throw new IndexOutOfRangeException("List is full");}items[count] = item;count++;}// 其他方法...
}// 使用示例
var intList = new GenericList<int>(10);
intList.Add(1);
intList.Add(2);var stringList = new GenericList<string>(5);
stringList.Add("Hello");
stringList.Add("World");
2. 类型安全
// 使用泛型集合,无需担心类型错误
List<int> intList = new List<int>();
intList.Add(1);
// intList.Add("string"); // 编译错误,因为集合是 int 类型的
3. 性能优化
// 使用泛型集合避免了装箱和拆箱操作
List<int> intList = new List<int>();
intList.Add(1);
int firstInt = intList[0]; // 直接访问,无需拆箱// 相比之下,使用非泛型集合会有装箱和拆箱开销
ArrayList arrayList = new ArrayList();
arrayList.Add(1); // 装箱
int firstValue = (int)arrayList[0]; // 拆箱
4. 减少代码冗余
// 泛型类可以替代多个具有相似功能的类
Stack<int> intStack = new Stack<int>();
intStack.Push(1);
int topInt = intStack.Pop();Stack<string> stringStack = new Stack<string>();
stringStack.Push("Hello");
string topString = stringStack.Pop();
5. 扩展性
public interface IRepository<T>
{T Get(int id);void Add(T item);// 其他方法...
}public class ProductRepository : IRepository<Product>
{// 实现 IRepository<Product> 接口的方法
}IRepository<Product> productRepo = new ProductRepository();
Product product = productRepo.Get(1);
6. 泛型约束示例
public class GenericComparer<T> where T : IComparable<T>
{public int Compare(T x, T y){return x.CompareTo(y);}
}var comparer = new GenericComparer<int>();
int result = comparer.Compare(1, 2); // 返回 -1
7. 与集合框架的集成
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
Dictionary<string, int> ages = new Dictionary<string, int>();
ages.Add("Alice", 30);
ages.Add("Bob", 25);var olderThan30 = ages.Where(entry => entry.Value > 30);
foreach (var pair in olderThan30)
{Console.WriteLine("{0} is {1} years old.", pair.Key, pair.Value);
}
8. 泛型委托示例
public delegate TResult GenericDelegate<T, TResult>(T arg);public class GenericClass
{public static int Double(int value){return value * 2;}
}GenericDelegate<int, int> del = GenericClass.Double;
int result = del(5); // 结果为 10
高级用法与技巧
泛型在反射中的应用
创建泛型类型的实例
using System;
using System.Reflection;public class GenericClass<T>
{public T Property { get; set; }public GenericClass(T property){Property = property;}
}class Program
{static void Main(){Type genericType = typeof(GenericClass<>);Type specificType = genericType.MakeGenericType(typeof(int));object instance = Activator.CreateInstance(specificType, 42);Console.WriteLine(((GenericClass<int>)instance).Property); // 输出: 42}
}
调用泛型方法
using System;
using System.Reflection;public class GenericMethodsClass
{public void GenericMethod<T>(T param){Console.WriteLine(param);}
}class Program
{static void Main(){var genericMethodsClass = new GenericMethodsClass();MethodInfo genericMethod = typeof(GenericMethodsClass).GetMethod("GenericMethod");MethodInfo specificMethod = genericMethod.MakeGenericMethod(typeof(string));specificMethod.Invoke(genericMethodsClass, new object[] { "Hello, World!" }); // 输出: Hello, World!}
}
协变和逆变
协变(Covariance)
using System;
using System.Collections.Generic;public class CovarianceExample
{public static void PrintValues<T>(IEnumerable<T> collection){foreach (var item in collection){Console.WriteLine(item);}}static void Main(){IEnumerable<string> strings = new List<string> { "A", "B", "C" };// 协变使得可以将IEnumerable<string>赋值给IEnumerable<object>IEnumerable<object> objects = strings;PrintValues(objects); // 输出: A, B, C}
}
逆变(Contravariance)
using System;
using System.Collections.Generic;public class ContravarianceExample
{public static void Compare<T>(T first, T second, IComparer<T> comparer){if (comparer.Compare(first, second) > 0){Console.WriteLine($"{first} is greater than {second}.");}else{Console.WriteLine($"{first} is less than or equal to {second}.");}}static void Main(){// 逆变使得可以将IComparer<object>赋值给IComparer<string>IComparer<object> objectComparer = Comparer<object>.Default;IComparer<string> stringComparer = objectComparer;Compare("apple", "orange", stringComparer); // 输出可能因默认比较器而异}
}
在这两个例子中,协变允许我们将一个更具体的集合类型(如IEnumerable<string>
)赋值给一个较不具体的类型(如IEnumerable<object>
)。而逆变则允许我们将一个较不具体的比较器类型(如IComparer<object>
)赋值给一个更具体的类型(如IComparer<string>
)。这两个特性在泛型编程中非常有用,因为它们提供了更大的灵活性和类型安全性。