How parameter by reference/value works in C#

自古美人都是妖i 提交于 2021-01-27 13:22:19

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!