[C#]泛型类与泛型方法

一、C++与C#泛型写法区别

复制代码
//C++ 写法(你已经会的)
template<tempname T>
class Stack
{
PUblic:
    void Push(T x)
    {
        Data.push_back(x);
    }
    T top()
    {
        T x = data.back();
        data.pop_back();
        return x;
    }
private:
    vector<T>Data;
}

// 泛型函数

template<tempname T>
void Print(T item)
{
    cout<<item<< endl;
}
Stack<int> si;       // 实例化
Stack<string> ss;

复制代码
//c#写法
class Stack<T>
{
    private List<T> _data = new();          // new() 目标类型 new,省类型名
    public void Push(T x) => _data.Add(x);
    public T Pop()
    {
         var item = _data[^1];               // [^1] = 最后一个,C# 索引运算符
         _data.RemoveAt(_data.Count - 1);
         return item;
    }
     public int Count => _data.Count;
}

// 泛型方法 ------ <T> 写在方法名后
void Print<T>(T item) => Console.WriteLine(item);

Stack<int> si = new();                      // 实例化,类型靠右边推断
Stack<string> ss = new();
1.1对照图

|--------------------------------------|-------------------------|---------------------------------------|
| C++ | C# | 备注 |
| template<typename T> | class Stack<T> | C# 把 <T> 直接写在类名后,没有 template<> 前缀 |
| Stack<int> | Stack<int> | 实例化语法完全一样 |
| template<typename T> void Print(T) | void Print<T>(T item) | C# 的 <T> 写在方法名和参数列表之间 |
| 编译期源码展开 | 运行时具化 | ⚠️ 见第 3 步,这是灵魂差异 |

💡 一个小甜点:C# 的 new() 叫目标类型 new,Stack<int> si = new(); 里右边不用再写 Stack<int>(),编译器看左边猜右边。C++17 的 Stack<int> si{}; 类似。


二、泛型集合

C# 日常 90% 的泛型用在这两个集合上,记住这个映射就行:

|---------------------------|-------------------------|---------------|
| C++ | C# | 说明 |
| std::vector<T> | List<T> | 动态数组,最常用 |
| std::unordered_map<K,V> | Dictionary<K,V> | 哈希表 |
| std::map<K,V> | SortedDictionary<K,V> | 有序(红黑树) |
| v.push_back(x) | list.Add(x) | |
| v.size() | list.Count | 注意是属性不是方法 |
| vi | listi | 索引访问一样 |
| v.back() | list\^1 | C# 用 ^ 从后往前数 |

复制代码
List<int> = new() {1,2,3};        //初始器
nums.Add(4);
Console.WriteLine(nums[^1]);               // 4,最后一个

Dictionary<string, int> ages = new()
{
    ["张三"] = 20;
    ["李四"] = 30;
};
Console.WriteLine(ages["张三"]);  // 20

三、核心差异------为什么 C# 泛型不能用 +?

这是今天最该花时间的地方。看这段对比:

4.1 C++ 模板:编译期源码展开(duck typing)
复制代码
template<typename T>
T Add(T a, T b) { return a + b; }    // 只要 T 有 operator+,就能编过

Add(3, 5);                 // OK,int 有 +
Add(string("a"), string("b"));  // OK,string 有 +
Add(Thing{}, Thing{});     // 只要 Thing 重载了 +,就 OK

C++ 编译器看到 Add<int>,就把模板里每个 T 替换成 int,生成一份专门的 Add 代码。它不检查「T 有没有 +」,它只是把代码抄一遍,等替换完再看 int + int 合不合法。模板的本质是高级的文本替换/代码生成。

4.2 C# 泛型:运行时具化(reified),编译期只认一份 IL
复制代码
T Add<T>(T a, T b) => a + b;   // ❌ 编译错误!运算符 '+' 不能应用于类型 'T'

为什么错?因为 C# 编译器在编译 Add<T> 时,它真的不知道 T 是什么。它只编译出一份泛型 IL 代码,这份代码要在运行时、当你传 int 进去时才「具化」成 Add<int>。编译期它没有任何理由相信 T 一定有 +------万一你传个 Person 进来呢?Person + Person 不存在啊。所以编译器直接拒绝。

这就是为什么 C# 泛型叫 reified(具化):泛型类型 List<int> 在运行时是一个真实存在的、具体的类型,而不是编译后就被擦除掉(Java 那种叫类型擦除 erasure)。但代价是------编译期对 T 能做什么一无所知,除非你用约束告诉它。

4.3 这个差异会带来什么后果?

|------------------|---------------|---------------------------------|
| 场景 | C++ 模板 | C# 泛型 |
| a + b | ✅ 只要 T 有 + 就行 | ❌ 直接报错 |
| new T() | ✅ 默认就行 | ❌ 要加 where T : new() 约束 |
| T t = null | ✅ 指针类型可以 | ❌ 要加 where T : class 约束 |
| 调 t.CompareTo(x) | ✅ 有这个方法就行 | ❌ 要加 where T : IComparable<T> |

四、总结

一句话总结:C++ 模板是「先抄代码再检查」,C# 泛型是「先检查约束再用」。C# 想用 T 的任何能力,都必须用 where 约束提前声明。你只要记住「C# 泛型对 T 一无所知」这个直觉就够了。

复制代码
using System;
using System.Collections.Generic;

// ---------- 1. 泛型类 Stack<T> ----------
class Stack<T>
{
    private readonly List<T> _items = new();

    public void Push(T item) => _items.Add(item);

    public T Pop()
    {
        if (_items.Count == 0)
            throw new InvalidOperationException("栈是空的!");
        var top = _items[^1];
        _items.RemoveAt(_items.Count - 1);
        return top;
    }

    public T Peek()
    {
        if (_items.Count == 0)
            throw new InvalidOperationException("栈是空的!");
        return _items[^1];
    }

    public int Count => _items.Count;
    public bool IsEmpty => Count == 0;
}

// ---------- 2. 泛型方法 ----------
class Printer
{
    // <T> 写在方法名后、参数表前。调用时通常可省略,靠参数推断
    public static void Print<T>(T item) => Console.WriteLine($"值 = {item},类型 = {typeof(T).Name}");
}

// ---------- 3. 主程序 ----------
class Program
{
     static void Main()
    {
        Console.WriteLine("===== 栈:int 类型 =====");
        Stack<int> intStack = new();
        intStack.Push(10);
        intStack.Push(20);
        intStack.Push(30);
        Console.WriteLine($"栈顶(不弹出): {intStack.Peek()}");
        Console.WriteLine($"弹出: {intStack.Pop()}");
        Console.WriteLine($"弹出: {intStack.Pop()}");
        Console.WriteLine($"剩余数量: {intStack.Count}");

        Console.WriteLine("\n===== 栈:string 类型(同一个类,不同 T)=====");
        Stack<string> strStack = new();
        strStack.Push("你好");
        strStack.Push("世界");
        Console.WriteLine($"弹出: {strStack.Pop()}");
        Console.WriteLine($"弹出: {strStack.Pop()}");

        // intStack.Push("字符串");  // ❌ 编译错误!类型安全:int 栈不能塞 string
        // 这就是泛型相比 C++ 模板的最大日常价值:编译期类型检查 + 无装箱

         Console.WriteLine("\n===== 泛型方法 =====");
        Printer.Print(42);              // 推断 T = int
        Printer.Print("泛型方法");      // 推断 T = string
        Printer.Print(3.14);            // 推断 T = double

        Console.WriteLine("\n===== List<T> / Dictionary<K,V> 快速体验 =====");
        List<string> names = new() { "张三", "李四", "王五" };
        names.Add("赵六");
        foreach (var n in names) Console.WriteLine($"  - {n}");

        Dictionary<string, int> score = new()
        {
            ["数学"] = 90,
            ["语文"] = 85
        };
        score["英语"] = 95;
        Console.WriteLine($"数学成绩: {score["数学"]}");
    }
}
相关推荐
脉动数据行情15 分钟前
C# 实现德指 DAX 期货 WebSocket 实时行情监听
开发语言·websocket·c#
码士集团小青15 分钟前
C# 手写向量化能追上 C 吗?
c#
大圣编蚕32 分钟前
Java ByteArrayInputStream 详解:从入门到实战
java·开发语言·算法
菜鸟~noob23340 分钟前
【电子战】第02篇:能量检测器 vs. 相关检测器【含matlab代码】
开发语言·matlab
OpenAnolis小助手2 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·阿里云·操作系统控制台·sysom
小玮看世界2 小时前
[Python]OD算法在OD实际运用转化参考清单
开发语言·python·算法
ForteScarlet2 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
萧瑟余晖2 小时前
Java深入解析篇六十二之安全机制详解
java·开发语言
c#上位机3 小时前
tuple_max求数组中的最大值
c#·halcon·机器视觉
老赵的博客3 小时前
c++ QT之动态库加载问题
c++·qt