问题
I have this sample code:
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();
}
}
I would like to understand why the output is 2 and not 3, could you please give me some clear explanation?
Thanks
回答1:
I will go through with you, step by step via the debugger and we will see what it is 2.
We see that we entered into Foo and we passed an instance of MyClass by reference v
(class instances in C# are passed by reference by default)
In memory, we would see something like this:
v = 0x01; //0x01 - is a simple representation of a pointer that we passed
v.Value = 1;
next, we step over and we see that we change the value Value in our reference.
v = 0x01;
v.Value = 2; // our new value
and then we assign new
to our v
so in memory we have
v* = 0x01 // this is our "old" object
v*.Value = 2;
v = 0x02 // this is our "new" object
v.Value = 3;
as you can see we have 2 objects in the memory! The new one v
and the old one marked with a start v*
when we exit the method, we didn't replace the content of the memory address 0x01
but created a local copy of v
for the scope of the functions, and we created a new object under the memory address 0x02
which is not referenced in our Main method.
Our main method is using instance from address 0x01
not new 0x02
we created in Foo method!
To make sure we pass the right object out, we need to tell C# that we want to either "edit" output using ref
or we want to "overwrite" output using out
.
Under the hood, they are implemented the same way!
Instead of passing 0x01
to our Foo method, we pass 0x03
! which has a pointer to our class under 0x01
. So when we assign v = new MyClass()
when we use ref
or out
, we, in reality, modify the value of 0x03
which is then extracted and "replaced" in our Main method to contain the proper value!
回答2:
Because when you call Foo(m)
, v
and m
are separate references to the same object.
Reassigning to v
does not reassign to m
.
Contrast this with the below:
public static void Foo(ref MyClass v)
{
v.Value = 2;
v = new MyClass();
v.Value = 3;
}
Through the use of ref
, if you now call Foo(m)
, v
and m
become the same reference to the same object, so reassigning to v
also reassigns to m
: making this output 3:
static void Main(string[] args)
{
var m = new MyClass();
m.Value = 1;
Foo(m);
Console.Write(m.Value);
Console.ReadLine();
}
回答3:
Initializing v
object inside Foo
function creates a new MyClass
instance however it's reference is not set to m
object inside Main
function. Because reference of object is passed by value.
If you want it to be referenced inside Foo
you should use ref
like this;
public static void Foo(ref MyClass v)
and call it like this;
Foo(ref m)
You can also use out
instead of ref
if parameter passed must be initizalied by the method.
回答4:
When you pass a reference to the method, that reference is copied into another variable on the stack. These two variables (references) might still reference the same object, but the variables themselves are different. It's the same as this:
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
You wouldn't expect to have m.Value
equals to 3
here, because you had two different variables which referenced the same object on the heap, but then you changed s
so that it references a brand new object. The same happens when you pass a reference to the method, it's just copied into another variable.
The main idea you can get from this is that instances of classes are passed by reference by default (because you actually pass reference), but the references themselves are passed by value, which means that they are copied into another variable.
回答5:
At the moment that v = new MyClass();
is called, inside Foo, the reference stops pointing to the object that was passed, instead it now points to the new object that was created.
This does not effect the caller, as the new object is not created at the memory that was allocated for the old one, rather the variable v now points to the new object, instead of the object that it used to point to.
That is why Foo effects the value to be 2, its the original object, but after the reassignment of v, the original object is not effected
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();
}
}
来源:https://stackoverflow.com/questions/65028431/how-parameter-by-reference-value-works-in-c-sharp