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(),在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方法中创建的address
0x01
而不是new
0x02
中的实例

为了确保传递正确的对象,我们需要告诉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;