C#中的泛型:初学者指南——为什么需要以及如何使用

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C#中的泛型是什么以及为什么需要它们



想象一下,您正在编写一个需要处理不同数据类型的方法:整数、字符串、对象。如果没有泛型,您要么需要为每种类型重复编写代码,要么使用object类型并进行后续的类型转换。这两种方法都会导致问题:前者导致代码冗余,后者导致运行时错误。



泛型是C#中的一种机制,它允许您创建能够处理任何数据类型,同时保持强类型安全的类、方法、接口和委托。您无需为特定类型编写代码,而是使用类型参数(通常用字母T表示),该参数在使用时会被实际类型替换。



泛型的主要优点:



  • 类型安全——编译器在编译时检查类型使用的正确性
  • 性能——对于值类型,避免了装箱/拆箱操作
  • 代码复用——一个类/方法可以处理多种类型
  • 可读性——代码更简洁、更清晰


没有泛型的问题:以object为例



在泛型出现之前,开发人员经常使用object类型来创建通用方法。来看一个例子:



public class Stack{    private object[] _items = new object[10];    private int _count = 0;        public void Push(object item) { _items[_count++] = item; }    public object Pop() { return _items[--_count]; }        // 使用    Stack stack = new Stack();    stack.Push(123);           // 将int装箱为object    stack.Push("hello");       // 可以添加任何类型    int number = (int)stack.Pop(); // 拆箱并进行类型转换}


问题显而易见:

  • 提取时需要显式类型转换
  • 可能会意外添加其他类型的值
  • 对于值类型(int, double),会发生装箱/拆箱,从而降低性能


C#中泛型的基础知识



创建泛型类



来看同一个栈的例子,但这次使用泛型:



public class Stack<T>{    private T[] _items = new T[10];    private int _count = 0;        public void Push(T item) { _items[_count++] = item; }    public T Pop() { return _items[--_count]; }}

// 使用Stack<int> intStack = new Stack<int>();intStack.Push(123);intStack.Push(456);int number = intStack.Pop(); // 无需拆箱,类型已知

Stack<string> stringStack = new Stack<string>();stringStack.Push("Hello");// stringStack.Push(123); // 编译错误!类型安全


参数<T>写在尖括号中。创建实例时,您指定具体类型,编译器会为该类型生成类的特化版本。



泛型方法



泛型不仅适用于类,也适用于单个方法:



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

// 使用int maxInt = Utils.Max(5, 10); // 10string maxStr = Utils.Max("apple", "banana"); // "banana"


泛型中的约束



有时需要限制可用于泛型的类型。这时可以使用约束,通过where关键字实现:



// 约束:T必须是引用类型public class Repository<T> where T : class{                

博客

书籍推荐