跳转到内容

C Sharp 泛型

来自代码酷

C#泛型[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

泛型(Generics)是C#语言中一项强大的特性,允许开发者编写可重用的、类型安全的代码,而无需为每种数据类型重复编写相同的逻辑。泛型通过使用类型参数(Type Parameters)来实现这一目标,使得类、接口、方法等可以在定义时不指定具体类型,而在使用时再确定类型。

泛型的主要优点包括:

  • 类型安全:编译时进行类型检查,避免运行时类型转换错误。
  • 代码重用:同一套逻辑可以应用于多种数据类型。
  • 性能优化:避免装箱和拆箱操作(对于值类型)。

基本语法[编辑 | 编辑源代码]

泛型通常通过尖括号 `<T>` 来声明类型参数,其中 `T` 是类型参数的名称(可以是任何有效的标识符)。

泛型类[编辑 | 编辑源代码]

以下是一个简单的泛型类示例:

public class GenericList<T>
{
    private T[] items;
    private int count = 0;

    public GenericList(int capacity)
    {
        items = new T[capacity];
    }

    public void Add(T item)
    {
        items[count++] = item;
    }

    public T GetItem(int index)
    {
        return items[index];
    }
}

使用泛型类[编辑 | 编辑源代码]

GenericList<int> intList = new GenericList<int>(5);
intList.Add(10);
intList.Add(20);
Console.WriteLine(intList.GetItem(0)); // 输出: 10

GenericList<string> stringList = new GenericList<string>(3);
stringList.Add("Hello");
stringList.Add("World");
Console.WriteLine(stringList.GetItem(1)); // 输出: World

泛型约束[编辑 | 编辑源代码]

泛型可以通过约束(Constraints)限制类型参数的范围,以确保类型参数符合特定条件。常见的约束包括:

  • `where T : struct` - T必须是值类型。
  • `where T : class` - T必须是引用类型。
  • `where T : new()` - T必须有无参构造函数。
  • `where T : BaseClass` - T必须继承自 `BaseClass`。
  • `where T : InterfaceName` - T必须实现 `InterfaceName` 接口。

示例:泛型约束[编辑 | 编辑源代码]

public class GenericCalculator<T> where T : IComparable<T>
{
    public T Max(T a, T b)
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
}

泛型方法[编辑 | 编辑源代码]

泛型方法允许在方法级别使用类型参数,而不需要整个类都是泛型的。

public static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

调用泛型方法[编辑 | 编辑源代码]

int x = 5, y = 10;
Swap(ref x, ref y);
Console.WriteLine($"x = {x}, y = {y}"); // 输出: x = 10, y = 5

string s1 = "Hello", s2 = "World";
Swap(ref s1, ref s2);
Console.WriteLine($"s1 = {s1}, s2 = {s2}"); // 输出: s1 = World, s2 = Hello

泛型接口[编辑 | 编辑源代码]

泛型接口允许定义通用的接口契约,适用于多种类型。

public interface IRepository<T>
{
    void Add(T entity);
    T GetById(int id);
    IEnumerable<T> GetAll();
}

实际应用案例[编辑 | 编辑源代码]

案例1:通用数据存储[编辑 | 编辑源代码]

泛型常用于实现数据存储结构,如 `List<T>`、`Dictionary<TKey, TValue>` 等。

List<int> numbers = new List<int> { 1, 2, 3 };
Dictionary<string, int> ages = new Dictionary<string, int>
{
    { "Alice", 25 },
    { "Bob", 30 }
};

案例2:泛型工厂模式[编辑 | 编辑源代码]

泛型可以用于实现工厂模式,动态创建不同类型的对象。

public class Factory<T> where T : new()
{
    public T CreateInstance()
    {
        return new T();
    }
}

泛型与性能[编辑 | 编辑源代码]

泛型在运行时不会产生额外的性能开销,因为C#编译器会为每个不同的值类型生成特定的代码(称为特化),而对于引用类型则共享同一份代码。

性能对比[编辑 | 编辑源代码]

泛型 vs 非泛型(装箱/拆箱)
方法 性能影响
需要装箱和拆箱,性能较差
无装箱/拆箱,性能更优

常见问题[编辑 | 编辑源代码]

1. 泛型与 `object` 的区别[编辑 | 编辑源代码]

  • 泛型是类型安全的,而 `object` 需要强制类型转换。
  • 泛型避免了装箱和拆箱操作(对于值类型)。

2. 泛型能否用于静态成员?[编辑 | 编辑源代码]

静态成员可以使用泛型类型参数,但静态成员本身不能是泛型的(除非在泛型类中)。

总结[编辑 | 编辑源代码]

泛型是C#中一项强大的特性,能够提高代码的复用性、类型安全性和性能。通过泛型类、泛型方法、泛型接口以及约束,开发者可以编写更加灵活和高效的代码。

进一步学习[编辑 | 编辑源代码]

  • 泛型集合(`List<T>`、`Dictionary<TKey, TValue>`)
  • 协变和逆变(`in` 和 `out` 关键字)
  • 泛型委托(`Action<T>`、`Func<T>`)