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

C#方法调用后输出与预期不符?Method3/Method4传参原理详解

C#传参机制与输出差异原因解释

核心前置规则

C#中所有参数默认采用值传递逻辑,传递的是参数存储内容的副本,根据参数类型不同,副本的内容有区别:

  • 值类型(如int、bool等):变量本身直接存储实例数据,值传递时副本就是数据本身,方法内修改副本完全不影响外部原变量
  • 引用类型(如自定义类A、string等):变量本身存储的是堆上对象实例的内存引用地址,值传递时副本是这个引用地址,方法内的参数副本和外部原变量初始指向堆上同一个对象实例
  • string作为特殊的不可变引用类型,任何对字符串的“修改”本质都是创建新字符串实例、改变变量指向的引用地址,和普通引用类型修改内部属性的逻辑不同

相关测试代码

public class A
{
    public int A1 { get; set; }
    public int A2 { get; set; }
}

static void Main(string[] args)
{
    int i = 0;
    Method1(i);
    Console.WriteLine($"i={i}");
    var str = "This is a string";
    Method2(str);
    Console.WriteLine(str);
    var a = new A()
    {
        A1 = 5,
        A2 = 6
    };
    Method3(a);
    Console.WriteLine($"a.A1={a.A1}, a.A2={a.A2}");
    Method4(a);
    Console.WriteLine($"a.A1={a.A1}, a.A2={a.A2}");
    Method5(ref a);
    Console.WriteLine($"a.A1={a.A1}, a.A2={a.A2}");
}

private static void Method1(int i)
{
    i = 5;
}

private static void Method2(string str)
{
    str = "This is a new string";
}

private static void Method3(A a)
{
    a.A1 = 6;
}

private static void Method4(A a)
{
    a = new A()
    {
        A1 = 10,
        A2 = 11
    };
}

private static void Method5(ref A a)
{
    a = new A()
    {
        A1 = 10,
        A2 = 11
    };
}

Method3 工作机制

  • 传参阶段:方法内的参数a是Main方法中a变量存储的引用地址的副本,二者初始指向堆上同一个A类实例(初始值A1=5、A2=6)
  • 执行阶段:a.A1 = 6是通过持有的引用地址找到堆上的实际对象,直接修改该实例的属性值,这个修改是持久作用在原实例上的
  • 执行结束:方法内参数副本被回收,外部Main中的a依然指向这个被修改过属性的实例,因此第一次打印类实例时输出a.A1=6, a.A2=6

Method4 工作机制

  • 传参阶段:方法内的参数a同样是Main中a存储的引用地址的副本,初始指向Method3修改过的那个A实例(A1=6、A2=6)
  • 执行阶段:a = new A() {A1=10, A2=11}是将方法内参数副本本身存储的引用地址,替换为新创建的A实例的地址。这个操作仅修改方法作用域内的局部参数,既没有改动外部Main中a存储的引用地址,也没有修改原来堆上的旧实例
  • 执行结束:方法内的参数副本出作用域被回收,外部Main中的a依然指向之前的旧实例,因此第二次打印类实例时输出还是a.A1=6, a.A2=6

输出差异的核心原因

预期输出和实际输出不符,来自两个认知偏差:

  1. 误认为引用类型默认值传递时,方法内无法修改原对象的状态:实际上只要没有给参数变量重新赋值新实例,通过引用副本访问、修改原实例的内部属性,会直接作用在原对象上,因此Method3会把原实例的A1从5改成6
  2. 忽略了Method3已经修改了原实例的属性值:Method4确实不会改变外部a的指向,但原实例的A1已经被Method3改成6,不会回到初始值5,因此不会出现预期中a.A1=5的输出

补充:Method5的ref传参逻辑

Method5使用ref关键字修饰参数时,不再传递引用地址的副本,而是直接传递外部变量本身的存储位置,方法内的a和Main中的a是同一个存储单元。此时给a赋值新实例,会直接修改外部变量的指向,因此最后打印时会输出新实例的属性a.A1=10, a.A2=11,这部分和预期一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:36:26