You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#委托复制行为疑问:引用类型为何表现类似值类型?

委托作为引用类型的特殊复制行为解析

先看你提供的C# 11示例代码:

file class C
{
    public int IntField1;
}

file struct S
{
    public int IntField1;
}

file class StructureVsClassWhenCopying
{
    private static void v1()
    {
        System.Console.WriteLine("v1");
    }

    private static void v2()
    {
        System.Console.WriteLine("v2");
    }

    public static void Main()
    {
        C c1 = new();
        c1.IntField1 = 1;
        C c2 = c1;
        c1.IntField1 = 2;
        System.Console.WriteLine(c2.IntField1); // 2, because class is a reference-type

        S s1 = new();
        s1.IntField1 = 1;
        S s2 = s1;
        s1.IntField1 = 2;
        System.Console.WriteLine(s2.IntField1); // 1, because struct is a value type

        string str1 = "old string";
        string str2 = str1;
        str1 = "new string";
        System.Console.WriteLine(str2); // old string, because string is immutable

        System.Action a1 = v1;
        System.Action a2 = a1;
        a1 -= v1;
        a1 += v2;
        a2.Invoke(); //v1. Why?
    }
}

你的核心疑问是:委托明明是引用类型,为什么复制后修改原委托,副本不受影响,表现得像值类型或不可变的字符串?

关键原因:委托是不可变的引用类型

和字符串一样,委托实例一旦创建就无法被修改——你看到的+=、-=操作,本质上并不是修改原有委托对象的内部状态,而是创建一个全新的委托实例,然后将这个新实例的引用赋值给原来的变量。

结合代码具体拆解:

  1. System.Action a1 = v1;:创建一个指向v1的委托实例,a1引用这个实例。
  2. System.Action a2 = a1;:a2和a1此时指向同一个委托实例(就是那个包含v1的实例)。
  3. a1 -= v1;:这个操作不会修改原来的委托实例,而是生成一个新的空委托实例,a1的引用被更新为指向这个新实例。
  4. a1 += v2;:再次生成一个新的委托实例(包含v2),a1的引用又指向这个新实例。

而a2自始至终都没有改变过引用指向,它一直指向最初那个包含v1的委托实例,所以调用a2.Invoke()自然会输出v1。

和普通引用类型的区别

普通的引用类型(比如示例中的C),当你修改对象的内部字段时,是直接操作引用指向的对象本身——所以所有指向该对象的变量都会看到变化。但委托的+=/-=是直接替换变量的引用目标,而非修改原对象,这就导致副本的引用不会跟着改变,看起来和值类型的复制行为类似。

内容的提问来源于stack exchange,提问作者user20311258

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 17:55:30