Files
2026-06-21 09:51:55 +08:00

6.6 KiB
Raw Permalink Blame History

泛型

概念: 泛型就是把==类型==参数化,让一份代码可以适配多种数据类型。 可以理解为:先把类型空出来,等使用时再指定具体类型。

例如:

List<int> nums = new List<int>();
List<string> names = new List<string>();

其中 intstring 就是传入泛型的具体类型。


为什么需要泛型

泛型的主要作用:

  1. 提高代码复用性
  2. 保证类型安全
  3. 减少强制类型转换
  4. 避免不必要的装箱和拆箱

对比 ArrayList

ArrayList list = new ArrayList();
list.Add(1);
list.Add("hello");

int num = (int)list[0];

ArrayList 内部存的是 object,取值时经常需要强制转换。

使用泛型集合:

List<int> list = new List<int>();
list.Add(1);

int num = list[0];

List<int> 只能存 int,编译器会提前帮我们检查类型错误。


常见泛型形式

泛型可以用于:

  1. 泛型类
  2. 泛型接口
  3. 泛型方法
  4. 泛型委托
  5. 泛型集合

常见泛型集合:

List<T>
Dictionary<TKey, TValue>
Queue<T>
Stack<T>
HashSet<T>

TTKeyTValue 都是泛型类型参数名。


语法

泛型类:

class 类名<T>
{
}

泛型接口:

interface 接口名<T>
{
}

泛型方法:

返回值类型 方法名<T>(T 参数)
{
}

多个泛型类型参数:

class 类名<T1, T2>
{
}

泛型类

泛型类适合用来保存或处理某种不确定类型的数据。

class Box<T>
{
    public T Value { get; set; }

    public Box(T value)
    {
        Value = value;
    }

    public T GetValue()
    {
        return Value;
    }
}

使用:

Box<int> intBox = new Box<int>(10);
Console.WriteLine(intBox.GetValue());

Box<string> stringBox = new Box<string>("hello");
Console.WriteLine(stringBox.GetValue());

理解:

  1. Box<T> 定义时不知道 T 是什么类型
  2. Box<int> 使用时把 T 替换成 int
  3. Box<string> 使用时把 T 替换成 string

泛型接口

泛型接口常用于规定一类对象必须提供某种泛型能力。

interface IRepository<T>
{
    void Add(T item);
    T Get(int index);
}

实现:

class MemoryRepository<T> : IRepository<T>
{
    private List<T> items = new List<T>();

    public void Add(T item)
    {
        items.Add(item);
    }

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

使用:

IRepository<string> repository = new MemoryRepository<string>();

repository.Add("小明");
Console.WriteLine(repository.Get(0));

泛型方法

泛型方法适合处理“逻辑一样,但参数类型不同”的情况。

static void Print<T>(T value)
{
    Console.WriteLine(value);
}

使用:

Print<int>(100);
Print<string>("hello");
Print<bool>(true);

很多时候,编译器可以自动推断泛型类型:

Print(100);
Print("hello");
Print(true);

泛型约束

默认情况下,泛型类型 T 可以是任意类型。

如果希望限制 T 必须满足某些条件,可以使用 where 约束。

语法:

class 类名<T> where T : 约束
{
}

常见约束:

约束 含义
where T : class T 必须是引用类型
where T : struct T 必须是值类型
where T : new() T 必须有无参构造函数
where T : 父类名 T 必须继承某个父类
where T : 接口名 T 必须实现某个接口

案例:

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

因为加了 where T : new(),所以可以在类中使用 new T()


多泛型参数

一个类或方法可以同时使用多个泛型参数。

class Pair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }

    public Pair(TKey key, TValue value)
    {
        Key = key;
        Value = value;
    }
}

使用:

Pair<int, string> pair = new Pair<int, string>(1, "小明");

Console.WriteLine(pair.Key);
Console.WriteLine(pair.Value);

default 关键字

泛型中有时不知道 T 的默认值是什么,可以使用 default

static T GetDefault<T>()
{
    return default(T);
}

常见默认值:

类型 默认值
int 0
bool false
引用类型 null
结构体 所有字段都是默认值

新语法也可以写成:

return default;

完整案例

using System;
using System.Collections.Generic;

namespace GenericsDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            DataStore<int> intStore = new DataStore<int>();
            intStore.Add(10);
            intStore.Add(20);

            Console.WriteLine(intStore.Get(0));
            Console.WriteLine(intStore.Get(1));

            DataStore<string> stringStore = new DataStore<string>();
            stringStore.Add("hello");
            stringStore.Add("world");

            Console.WriteLine(stringStore.Get(0));
            Console.WriteLine(stringStore.Get(1));

            Print(123);
            Print("泛型方法");
        }

        static void Print<T>(T value)
        {
            Console.WriteLine(value);
        }
    }

    interface IDataStore<T>
    {
        void Add(T value);
        T Get(int index);
    }

    class DataStore<T> : IDataStore<T>
    {
        private List<T> values = new List<T>();

        public void Add(T value)
        {
            values.Add(value);
        }

        public T Get(int index)
        {
            return values[index];
        }
    }
}

理解:

  1. DataStore<T> 是泛型类
  2. IDataStore<T> 是泛型接口
  3. Print<T> 是泛型方法
  4. DataStore<int> 只能保存整数
  5. DataStore<string> 只能保存字符串

注意

  1. 泛型类型参数常用 T 表示,但不是固定写法
  2. 多个泛型参数常写成 TKeyTValueTItem
  3. List<T>ArrayList 更常用,也更安全
  4. 泛型在编译期就能检查类型错误
  5. 使用泛型可以减少 object 带来的强制转换
  6. 如果需要在泛型中 new T(),必须加 where T : new() 约束
  7. 泛型不是“任何类型都乱放”,而是“使用时确定一种类型”

小结

一句话: 泛型就是==把类型当作参数传进去==。

记忆: 普通参数控制“值”。 泛型参数控制“类型”。

最常见写法:

List<int> nums = new List<int>();
Dictionary<string, int> scores = new Dictionary<string, int>();