reference/value 的参数如何在 C# 中工作
How parameter by reference/value works in C#
我有这个示例代码:
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
以便它引用一个全新的对象。当您将引用传递给该方法时,也会发生同样的情况,它只是被复制到另一个变量中。
你可以从中得到的主要思想是类的实例被传递给reference 默认情况下(因为你实际上传递了 reference),但是 references 本身是通过 by value 传递的,这意味着它们是复制到另一个变量中。
在调用 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();
}
我将通过调试器一步一步地与您一起完成,我们将看到它是什么 2.
我们看到我们进入了 Foo 并通过引用传递了 MyClass 的实例 v
(class C# 中的实例默认通过引用传递)
在记忆中,我们会看到这样的东西:
v = 0x01; //0x01 - is a simple representation of a pointer that we passed
v.Value = 1;
接下来,我们跨过一步,我们看到我们更改了引用中的值 Value。
v = 0x01;
v.Value = 2; // our new value
然后我们将 new
分配给我们的 v
所以在内存中我们有
v* = 0x01 // this is our "old" object
v*.Value = 2;
v = 0x02 // this is our "new" object
v.Value = 3;
如您所见,内存中有 2 个对象!新的v
和标有开头的旧的v*
当我们退出方法时,我们并没有替换内存地址0x01
的内容,而是为函数的作用域创建了一个v
的本地副本,并且我们创建了一个新的内存地址 0x02
下的对象,我们的 Main 方法中没有引用它。
我们的主要方法是使用来自地址 0x01
的实例,而不是我们在 Foo 方法中创建的新 0x02
!
为了确保我们传递正确的对象,我们需要告诉 C# 我们想要使用 ref
“编辑”输出或者我们想要使用 out
“覆盖”输出。
在后台,它们的实现方式相同!
我们没有将 0x01
传递给我们的 Foo 方法,而是传递了 0x03
!它有一个指向 0x01
下的 class 的指针。因此,当我们在使用 ref
或 out
时分配 v = new MyClass()
时,实际上我们修改了 0x03
的值,然后在我们的 Main 方法中将其提取并“替换”包含正确的值!
正在 Foo
函数内初始化 v
对象会创建一个新的 MyClass
实例,但它的引用未设置为 Main
函数内的 m
对象。因为对象的引用是按值传递的。
如果你想在 Foo
中引用它,你应该像这样使用 ref
;
public static void Foo(ref MyClass v)
然后这样称呼它;
Foo(ref m)
如果传递的参数必须由方法初始化,您也可以使用out
代替ref
。
我有这个示例代码:
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
以便它引用一个全新的对象。当您将引用传递给该方法时,也会发生同样的情况,它只是被复制到另一个变量中。
你可以从中得到的主要思想是类的实例被传递给reference 默认情况下(因为你实际上传递了 reference),但是 references 本身是通过 by value 传递的,这意味着它们是复制到另一个变量中。
在调用 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();
}
我将通过调试器一步一步地与您一起完成,我们将看到它是什么 2.
我们看到我们进入了 Foo 并通过引用传递了 MyClass 的实例 v
(class C# 中的实例默认通过引用传递)
在记忆中,我们会看到这样的东西:
v = 0x01; //0x01 - is a simple representation of a pointer that we passed
v.Value = 1;
接下来,我们跨过一步,我们看到我们更改了引用中的值 Value。
v = 0x01;
v.Value = 2; // our new value
然后我们将 new
分配给我们的 v
所以在内存中我们有
v* = 0x01 // this is our "old" object
v*.Value = 2;
v = 0x02 // this is our "new" object
v.Value = 3;
如您所见,内存中有 2 个对象!新的v
和标有开头的旧的v*
当我们退出方法时,我们并没有替换内存地址0x01
的内容,而是为函数的作用域创建了一个v
的本地副本,并且我们创建了一个新的内存地址 0x02
下的对象,我们的 Main 方法中没有引用它。
我们的主要方法是使用来自地址 0x01
的实例,而不是我们在 Foo 方法中创建的新 0x02
!
为了确保我们传递正确的对象,我们需要告诉 C# 我们想要使用 ref
“编辑”输出或者我们想要使用 out
“覆盖”输出。
在后台,它们的实现方式相同!
我们没有将 0x01
传递给我们的 Foo 方法,而是传递了 0x03
!它有一个指向 0x01
下的 class 的指针。因此,当我们在使用 ref
或 out
时分配 v = new MyClass()
时,实际上我们修改了 0x03
的值,然后在我们的 Main 方法中将其提取并“替换”包含正确的值!
正在 Foo
函数内初始化 v
对象会创建一个新的 MyClass
实例,但它的引用未设置为 Main
函数内的 m
对象。因为对象的引用是按值传递的。
如果你想在 Foo
中引用它,你应该像这样使用 ref
;
public static void Foo(ref MyClass v)
然后这样称呼它;
Foo(ref m)
如果传递的参数必须由方法初始化,您也可以使用out
代替ref
。