C#中object为引用类型,修改方法内参数后外部原值为何不变
现象原因
这个问题的核心是两个容易混淆的C#语法特性:引用类型的参数默认按值传递,以及值类型的装箱逻辑,具体拆解如下:
- 首先明确C#的参数传递规则:所有参数默认都是按值传递,即便参数类型是引用类型也不例外。所谓「引用类型传递的是对象引用」指的是传递的值本身是引用地址,但这个引用地址是从原变量复制出来的副本,和原变量是两个独立的存储单元。
- 第一步
object o=100执行时,因为int是值类型,赋值给引用类型object会触发装箱操作:在堆内存中生成一个包装了100的装箱对象,栈上的变量o存储的是这个装箱对象的内存地址。 - 调用
ChangeReferenceObj(o)时,会把o存储的地址复制一份传给形参a,此时a和o指向同一个堆上的100装箱对象,因此方法内第一次输出e100符合预期。 - 执行
a=500时,会生成一个新的包装了500的装箱对象,然后把新对象的地址赋值给形参a。注意这里修改的是形参a本身存储的地址值,而a是o的副本,二者是独立的栈变量,修改a的指向完全不会影响外部o的指向,o仍然指向原来的100装箱对象。
补充:如果你在方法内不是给
a整体赋值新对象,而是修改a指向的对象的成员属性/字段,外部变量会同步变化,因为二者此时仍然指向同一个堆对象。只有修改形参本身的指向(也就是给形参整体赋值新对象)才不会同步到原变量。
- 方法执行结束回到Main方法后,
o的指向没有发生任何变化,因此最终输出d100。
复现预期效果的方案
如果希望修改形参指向后同步修改外部变量,需要使用ref关键字声明按引用传递:
// 修改方法声明,添加ref关键字 static void ChangeReferenceObj(ref object a) { Console.WriteLine("e"+a); a=500; Console.WriteLine("f"+a); } // 调用时也需要添加ref关键字 ChangeReferenceObj(ref o);
此时传递的不再是引用的副本,而是外部变量o本身的存储地址,修改形参a的指向等价于直接修改外部o的指向,最终会输出d500。
内容的提问来源于stack exchange,提问作者Tommy1987
相关产品推荐
相关产品推荐

