C# 泛型

发布于:2024-07-11 ⋅ 阅读:(21) ⋅ 点赞:(0)

泛型

泛型不是语法糖,而是由框架提供的一种便捷语法,首次出现在.NET 2.0中。

1. 泛型定义

  • 泛型:是一种程序特性,定义时不对类型做出明确的规定,使用时规定且不能改变。
  • 一般应用:泛型集合、泛型方法、泛型类、泛型委托。
    • 泛型方法:方法的返回值类型、参数类型定义成泛型类型。
    • 泛型集合:如List<T>Dictionary<K, V>,所在命名空间:System.Collections.Generic
    • 非泛型集合:ArrayListHashtable,所在命名空间: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>)。这两个特性在泛型编程中非常有用,因为它们提供了更大的灵活性和类型安全性。