Why is the original object changed after a copy, without using ref arguments?

后端 未结 4 2048
渐次进展
渐次进展 2020-12-16 08:08

At work we were encountering a problem where the original object was changed after we send a copy through a method. We did find a workaround by using IClonable

相关标签:
4条回答
  • 2020-12-16 08:36

    It is always interesting to explain how this works. Of course my explanation could not be on par with the magnificiency of the Jon Skeet one or Joseph Albahari, but I would try nevertheless.

    In the old days of C programming, grasping the concept of pointers was fundamental to work with that language. So many years are passed and now we call them references but they are still ... glorified pointers and, if you understand how they work, you are half the way to become a programmer (just kidding)

    What is a reference? In a very short answer I would tell. It is a number stored in a variable and this number represent an address in memory where your data lies.
    Why we need references? Because it is very simple to handle a single number with which we could read the memory area of our data instead of having a whole object with all its fields moved along with our code.

    So, what happens when we write

    var myclass = new MyClass();
    

    We all know that this is a call to the constructor of the class MyClass, but for the Framework it is also a request to provide a memory area where the values of the instance (property, fields and other internal housekeeping infos) live and exist in a specific point in time. Suppose that MyClass needs 100 bytes to store everything it needs. The framework search the computer memory in some way and let's suppose that it finds a place in memory identified by the address 4200. This value (4200) is the value that it is assigned to the var myclass It is a pointer to the memory (oops it is a reference to the object instance)

    Now what happens when you call?

    var copy = myclass;
    

    Nothing particular. The copy variable gets the same value of myclass (4200). But the two variables are referencing the same memory area so using one or the other doesn't make any difference. The memory area (the instance of MyClass) is still located at our fictional memory address 4200.

    myclass.Mystring = "jadajadajada";
    

    This uses the reference value as a base value to find the area of memory occupied by the property and sets its value to the intern area where the literal strings are kept. If I could make an analogy with pointers it is as you take the base memory (4200), add an offset to find the point where the reference representing the propery MyString is kept inside the boundaries of the 100 bytes occupied by our object instance. Let's say that the MyString reference is 42 bytes past the beginning of the memory area. Adding 42 to 4200 yelds 4242 and this is the point in which the reference to the literal "jadajadajada" will be stored.

    Dal.DoSomeThing(copy);
    

    Here the problem (well the point where you have the problem). When you pass the copy variable don't think that the framework repeat the search for a memory area and copy everything from the original area in a new area. No, it would be practically impossible (think about if MyClass contains a property that is an instance of another class and so on... it could never stop.) So the value passed to the DoSomeThing method is again the reference value 4200. This value is automatically assigned to the local variable daclass declared as the input parameter for DoSomething (It is like you have explicitly done before with var copy = myclass;.

    At this point it is clear that any operation using daClass acts on the same memory area occupied by the original instance and you see the results when code returns back to your starting point.

    I beg the pardon from the more technically expert users here. Particularly for my casual and imprecise use of the term 'memory address'.

    0 讨论(0)
  • 2020-12-16 08:37

    The docs at MSDN say it pretty clearly. Value types are passed as a copy by default, objects are passed as a reference by default. Methods in C#

    0 讨论(0)
  • 2020-12-16 08:40

    that's normal since your MyClass is a reference type so you are passing a reference to original data not the data itself this why it's an expected behavior here is an explanation of what a reference type is from Parameter passing in C#

    A reference type is a type which has as its value a reference to the appropriate data rather than the data itself

    0 讨论(0)
  • 2020-12-16 08:41

    I see two issues here...

    Making a Copy of an object

    var copy = myClass; does not make a copy - what it really does is create a second reference ("pointer") to myClass (naming the variable "copy" is misleading). So you have myClass and copy pointing to the same exact object.

    To make a copy you have to do something like:

    var copy = new MyClass(myClass);
    

    Notice that I created a new object.

    Passing By Reference

    1. When passing value type variables without ref, the variable cannot be changed by the the receiving method.

      Example: DoSomething(int foo) - DoSomething cannot affect the value of foo outside of itself.

    2. When passing value type variables with ref, the variable can be changed

      Example: DoSomething(ref int foo) - if DoSomething changes foo, it will remain changed.

    3. When passing an object without ref, the object's data can be changed, but the reference to the object cannot be changed.

    void DoSomething(MyClass myClass)
    {
        myClass.myString = "ABC"   // the string is set to ABC
        myClass = new MyClass();   // has no affect - or may not even be allowed
    }
    
    1. When passing an object with ref, the object's data can be changed, and the reference to the object can be changed.
    void DoSomething(ref MyClass myClass)
    {
        myClass.myString = "ABC"   // the string is set to ABC
        myClass = new MyClass();   // the string will now be "" since myClass has been changed
    }
    
    0 讨论(0)
提交回复
热议问题