4

Take the following code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyClass
{
    public int myNumber;
}

public class Test : MonoBehaviour
{
    MyClass class1 = new MyClass();
    MyClass class2 = new MyClass();

    void Start()
    {
        class1.myNumber = 1;
        class2 = class1;
        class2.myNumber = 2;

        Debug.Log(class1.myNumber); //output: 2 (I'd want it to output 1!!!)
    }
}

Now, if I understood correctly, in C# classes are a reference type and not a value type, so this behavior is desired.
However, what would I need to do to not make it so? What could I do to make a normal value assignment without "linking" the two classes together?

I realize I could replace line 18 with this:

class2.myNumber = class1.myNumber;

And it would do what I want (since I'm assigning a value type and not a reference type), but it's kinda inconvenient for my case... in my real script the class has several sub-elements that I need to copy in many different places.

Is there a one-line method to copy a class to a new one, without it being linked by reference to the original one but by creating a new independent class?

man-teiv
  • 419
  • 3
  • 16
  • Would using a `struct` be acceptable? – rhughes Apr 15 '20 at 15:24
  • 1
    I'm not too familiar with C# but it is much like Java. So https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netframework-4.8 – jiveturkey Apr 15 '20 at 15:24
  • It's called a Deep Copy or Clone. You can look that up. The quick and dirty way is to serialize to json then back to a class instance. – Crowcoder Apr 15 '20 at 15:26
  • @rhughes that's interesting. What are the differences with a class? Does it behave the same way, except by being a value type and not a reference type? – man-teiv Apr 15 '20 at 15:26
  • If you don't use deep copy, you are basically just pointing to the same instance as it is a reference type, aka memory address. So if you change the value for one, it changes the values for the other. Deep Copy prevents this from happening. The alternative is called a Shallow Copy. – MattE Apr 15 '20 at 15:32
  • Another possible solution could be using immutable types. If an object can't change, your problem is solved. Without more information its hard to say if this option is a reasonable approach or not. – InBetween Apr 15 '20 at 15:57
  • @InBetween actually, for my scripts one of the two classes needs no change in time, while the other one does... is there a way to make only one immutable? – man-teiv Apr 15 '20 at 16:07
  • If it doesn't change, there is no need to do anything, even if its not immutable you dont have the problem you are asking about. About making a type immutable or not, it depends entirely on how its implemented, its not an option you can switch on or off on any given type or project. If the implementation is not yours, then there is no way to make a mutable type immutable; you need to design it that way. – InBetween Apr 15 '20 at 16:19

3 Answers3

1

C# has something called a Struct which is basically a lightweight version of a Class that is a value type. If you specifically need a value type you might want to use Structs instead of Classes.

Main differences are Structs don't need to be instantiated using a "New" assignment, they cannot inherit from another Struct or Class and they cannot be used as the base for another Class.

MattE
  • 1,044
  • 1
  • 14
  • 34
  • 2
    Structs can still have reference type properties so just using a Struct is not the whole answer. – Crowcoder Apr 15 '20 at 15:27
  • @Crowcoder can you expand a bit on that? Which properties still have reference type? – man-teiv Apr 15 '20 at 15:43
  • 1
    @Francesco What he's saying is that changing to a struct is not a general solution. If a struct holds a reference to a mutable reference type object, then a change in that object will be "seen" by all copies of that given value type independently of structs having copy by value semantics. – InBetween Apr 15 '20 at 15:48
  • 1
    There are significant differences between how classes and structs behave in .Net. Changing a type to struct simply to avoid the way .Net handles reference and value types is not a good design decision. Be sure that you fully understand the trade-offs you're making before going this route. Start reading [here](https://msdn.microsoft.com/en-us/library/ms229017(v=vs.110).aspx) and continue with several of the common C# class vs struct questions on StackOverflow. – Patrick Tucci Apr 15 '20 at 16:34
1

Perform a Deep or Shallow Copy depending on your needs. https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netframework-4.8

jiveturkey
  • 2,484
  • 1
  • 23
  • 41
  • Thank you @jiveturkey, I've accepted your answer because it's the right one, in fact in the example script it works. But for the love of everything's sacred, on my main script it doesn't! I'll keep researching, but I'll go in this direction. Just to make sure, if my class has only string, int and int[] types, can I simply use a ShallowCopy? – man-teiv Apr 15 '20 at 17:19
  • I believe that's what you want. Double check string though. It may be an object and not a base type. – jiveturkey Apr 15 '20 at 17:23
  • I think I found out my problem. Apparently arrays are reference type. I'm trying to readapt the DeepCopy method to see if I can fit it with those. – man-teiv Apr 15 '20 at 17:30
0

How about this?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyClass
{
    public int myNumber;
}

public class Test : MonoBehaviour
{
    MyClass class1 = new MyClass();
    MyClass class2 = new MyClass();

    private void Start()
    {
        class1.myNumber = 1;
        class2 = GetClassCopy(class1);
        class2.myNumber = 2;
        Debug.Log(class1.myNumber); // output:1
    }

    private MyClass GetClassCopy(MyClass source)
    {
        MyClass result = new MyClass();
        result.myNumber = source.myNumber;
        return result;
    }
}
Armin
  • 576
  • 6
  • 13