C# C中引用/值参数的工作原理#
我有以下示例代码:C# C中引用/值参数的工作原理#,c#,parameter-passing,pass-by-reference,pass-by-value,C#,Parameter Passing,Pass By Reference,Pass By Value,我有以下示例代码: public class MyClass { public int Value { get; set; } } class Program { public static void Foo(MyClass v) { v.Value = 2; v = new MyClass(); v.Value = 3;
public class MyClass
{
public int Value { get; set; }
}
class Program
{
public static void Foo(MyClass v)
{
v.Value = 2;
v = new MyClass();
v.Value = 3;
}
static void Main(string[] args)
{
var m = new MyClass();
m.Value = 1;
Foo(m);
Console.Write(m.Value);
Console.ReadLine();
}
}
我想了解为什么输出是2而不是3,你能给我一些明确的解释吗
谢谢当您传递对该方法的引用时,该引用将复制到堆栈上的另一个变量中。这两个变量(引用)可能仍然引用同一个对象,但变量本身是不同的。与此相同:
var m = new MyClass();
m.Value = 1;
var s = m;
s.Value = 2; // m.Value is also 2
s = new MyClass();
s.Value = 3; // m.Value is still 2
在这里,您不希望m.Value
等于3
,因为您有两个不同的变量引用堆上的同一对象,但随后您更改了s
,使其引用一个全新的对象。当您将引用传递给该方法时也会发生同样的情况,它只是复制到另一个变量中
您可以从中得到的主要思想是,类的实例在默认情况下通过引用传递(因为您实际上传递了引用),但是引用本身通过值传递,这意味着它们被复制到另一个变量中。此时
v=new MyClass()调用code>,在Foo内部,引用不再指向传递的对象,而是指向创建的新对象
这不会影响调用者,因为新对象不是在为旧对象分配的内存中创建的,而是变量v现在指向新对象,而不是它用来指向的对象
这就是为什么Foo将值影响为2,它是原始对象,但在重新指定v之后,原始对象不受影响
public class MyClass
{
public int Value { get; set; }
}
class Program
{
public static void Foo(MyClass v)
{
v.Value = 2;
v = new MyClass(); // this will make v point to an other object
v.Value = 3;
}
static void Main(string[] args)
{
var m = new MyClass();
m.Value = 1;
Foo(m);
Console.Write(m.Value);
Console.ReadLine();
}
}
因为在调用Foo(m)
时,v
和m
是对同一对象的单独引用
重新分配给v
不会重新分配给m
将此与以下内容进行对比:
public static void Foo(ref MyClass v)
{
v.Value = 2;
v = new MyClass();
v.Value = 3;
}
通过使用ref
,如果现在调用Foo(m)
,v
和m
成为对同一对象的相同引用,因此重新分配给v
也会重新分配给m
:使此输出3:
static void Main(string[] args)
{
var m = new MyClass();
m.Value = 1;
Foo(m);
Console.Write(m.Value);
Console.ReadLine();
}
我将通过调试器一步一步地介绍给您,我们将看到它是什么
我们看到我们进入了Foo,通过引用传递了MyClass的一个实例v
(默认情况下,C#中的类实例通过引用传递)
在记忆中,我们会看到这样的东西:
v = 0x01; //0x01 - is a simple representation of a pointer that we passed
v.Value = 1;
接下来,我们跨过一步,看到我们更改了引用中的值。
然后我们将new
分配给v
,这样我们就有了内存
v* = 0x01 // this is our "old" object
v*.Value = 2;
v = 0x02 // this is our "new" object
v.Value = 3;
如你所见,我们的内存中有两个对象!新的v
和旧的标有start的v*
当我们退出该方法时,我们没有替换内存地址0x01
的内容,而是为函数范围创建了v
的本地副本,并在内存地址0x02
下创建了一个新对象,该对象在我们的主方法中没有引用
我们的主要方法是使用我们在Foo方法中创建的address0x01
而不是new0x02
中的实例
为了确保传递正确的对象,我们需要告诉C,我们想要使用ref
来“编辑”输出,或者我们想要使用out
来“覆盖”输出
在引擎盖下,它们的实现方式相同
我们不是将0x01
传递给我们的Foo方法,而是传递0x03
!它在0x01
下有一个指向我们类的指针。因此,当我们在使用ref
或out
时分配v=new MyClass()
时,实际上,我们修改0x03
的值,然后在主方法中提取并“替换”该值,以包含正确的值 初始化v
函数内的对象Foo
会创建一个新的MyClass
实例,但其引用未设置为m
函数内的对象Main
函数。因为对象的引用是通过值传递的
如果你想在Foo
中引用它,你应该像这样使用ref
公共静态无效Foo(参考MyClass v)
这样称呼它
Foo(参考m)
如果传递的参数必须由该方法初始化,您也可以使用out
而不是ref
。“我们通过引用传递了v”-我猜v
更多的是一个引用,而不是一个实例,这是通过值传递的(这就是为什么这个问题有意义),但是v
引用的类的实例是通过引用传递的(即通过v
),正如您在parentheses@E.Shcherbo你说得对v
是一个引用-它是一个指向类实例的值,但可以看到您的句子是多么复杂!我花了很长时间才明白!我同意,也很抱歉,我不是想说你写错了什么,我只是写了一个结论,这个结论曾经帮助我完全理解了通过引用和价值传递,所以可能对其他人也有用:)
v* = 0x01 // this is our "old" object
v*.Value = 2;
v = 0x02 // this is our "new" object
v.Value = 3;