Help with copying a class without referencing

So, I’m having a little trouble wrapping my head around this one. I have a list of classes which contain all sorts of junk I’m using to create weapons with. When I duplicate this list in the inspector, it creates an entirely separate copy which has nothing to do with the original list (what I want), but when I try to create a copy using code, it references the original list, so that any change I make to the copy effects the original (not what I want). I’ve tried several examples I’ve seen of “deep cloning” and the like, but given the wide range of types of variables and references in the class, this hasn’t worked out.

My question is, is there a way to simply copy the class in the same way it’s done in the inspector through code at runtime? I’m not sure what kind of voodoo Unity does in the background to make this happen, but it sure would be useful if we had access to it through code. Thanks in advance.

Unity serializes the thing you selected into memory when you hit Copy. That is a deep copy. When it pastes, it deserializes the clipboard into a whole new independent data structure.

If you’ve tried to implement your own deep copy, everything you copy has to be deep copied, not just the top layer. Things that are primitive value types like integers and floats will copy by value, things that are a struct type will copy by value. You just need to be sure you’re copying any class elements deeply (and whatever class objects referenced inside those, and so on). It can get very complicated to do this right, especially if two things inside your data structure refer to each other. All those potential bugs and quirks have already been squashed and implemented and tested in the serialization mechanism.

You can serialize too. Either with Unity’s built-in UnityEngine.JsonUtility or the Newtonsoft.JSON package that is also now included in Unity’s built-in package registry.

4 Likes

For anything inherited from UnityEngine.Object you use Object.Instantiate.

For anything else, you need to set-up a deep copy on your own.

No idea what you mean here apart from maybe confusion on what a C# reference is.

2 Likes

I see. So I take that to mean that there is no built in function for this that I’m missing. I find that a bit strange honestly, considering even at run time, you can simply hit the plus icon on a list and it will auto-copy the last entry. You would think we’d have access to something similar via code. Thanks for the explanation!

Poor wording on my part. By that I meant to say that any changes I make to the copy, affect the original as well.

Well, C# at it’s core does not provide any general way to clone or copy a class instance. That’s completely up to the class to provide this functionality. If a class is serializable you can most of the time go though the serialization system. The Unity editor has several tools to copy serialized data. Most can be found in the EditorUtility. For normal serializable classes there’s EditorUtility.CopySerializedManagedFieldsOnly, for things derived from UnityEngine.Object you would usually use Instantiate. But if you just want to copy the values from one instance to another there’s also EditorUtility.CopySerialized. Though as I said, those are editor only methods. They can not be used at runtime.

“Hitting the plus sign” is also part of the editor / inspector which works on the serialized data anyways.

Even in languages what somewhat have the concept of a copy-constructor, “copying” class instances can be done in very different ways (shallow copy, deep copy). Such a system usually can’t know if you want to keep references to other objects or of those other objects should be cloned as well. What about internal / private fields? There are too many questions open to solve this in a general way.

The JsonUtility certainly is an option to copy a serializable class. Though you can of course provide your own “copy constructor” by adding a constructor to your data class that takes an instance of the same type. Now you can decide what data should be copied and in which way. Another “option” would be to use a “wrapper” ScriptableObject that simply holds a reference to your custom serializable class and simply Instantiate / clone that scriptable object and extract the refernced class. Though going through JsonUtility or Newtonsoft’s Json.NET is probably simpler. The two SOs would need to be destroyed once you’re done. (When you use a SO with a SerializeReference “object” field that SO could be used for any kind of class I guess. Never tried it though)

3 Likes

Note that when you hit that + sign it’s dealing with the SerializedObject and SerializedProperty:
https://docs.unity3d.com/ScriptReference/SerializedObject.html
https://docs.unity3d.com/ScriptReference/SerializedProperty.html

Since it’s already working with the serialized representation of the object it can easily duplicate a serialized version of it.

As for doing this at runtime, as MelvMay pointed out you can call Instantiate on a UnityEngine.Object to clone it effectively. Getting a runtime equivalent in behavior. Though this works only when the root object you’re cloning is a UnityEngine.Object.

Both of these a wholly dependent on the unity engine and how it behaves. This is also why it pertains to UnityEngine.Objects and their members. Note that it also is restricted to the serialization restrictions of these objects (for example Dictionary support is not inherent and instead must be manually performed via the serialization callbacks). Plane old C# class objects can be treated if and only if they are members of the UnityEngine.Object member hierarchy.

As for cloning plane old C# class objects independent of the unity engine.

Well… Halley implied a way. If you know your plane old C# objects are serializable to json you could technically jsonify it and then deserialize it back to a new object. That is an approach one could take, if only feeling a bit overkillish. But it’s easy.

Again though it will fully rely on the restrictions of the serialization you’re using. In this case all objects in the member hierarchy must be serializable.

Alternatively you have to perform a “deep clone” which you mentioned above.

A deep clone is called such as an alternative to a ‘shallow clone’. C# has built in a simple ‘shallow clone’ method:
object.MemberwiseClone:
https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=net-8.0

Note that it is solely available as a protected member of the object class. Meaning a method that accesses it must be a member of the class.

So generally you implement a ‘Clone’ method and call MemberwiseClone in it, and do any extra lifting you have to:

public class MyClass : ICloneable
{
    public int Value;

    public MyClass Clone()
    {
        return (MyClass)this.MemberwiseClone();
    }
    object ICloneable.Clone() => this.Clone();

}

Thing is if MyClass contains a reference type it won’t be cloned. Since MemberwiseClone is a shallow copy. Instead you’ll have to clone that object as well.

public class MyClass : ICloneable
{
    public int Value;
    public MyOtherClass Obj;

    public MyClass Clone()
    {
        var inst = (MyClass)this.MemberwiseClone();
        inst.Obj = inst.Obj?.Clone(); //this method isn't guaranteed to exist unless it's is defined on MyOtherClass
        return inst;
    }
    object ICloneable.Clone() => this.Clone();

}

Unfortunately though this will only work if every ref object in the member hierarchy is “cloneable”, and has a ‘Clone’ object defined on it.

Basically there is a lot of setup.

This is just a quark of C# and the fact it explicitly treats Classes as reference types always. You can’t simply dereference them and copy them in memory like you might in something like C++.

So from here we get into the complex topic of deep cloning objects and well… there’s a lot of magical ways you can do things. And honestly it’s getting into the weeds in the same way just serializing to json has, just each with their own ‘gotchas’. Here is a random article I found with a very quick google:
https://medium.com/@dayanandthombare/object-cloning-in-c-a-comprehensive-guide-️-️-d3b79ed6ebcd

Note it has examples using Emit (not suitable a lot in C#), reflecting out the fields, reflecting out the MemberwiseClone, and more.

[edit] - so I see some posts appeared while I was typing this and eating lunch… so if I repeated anyone. My apologies.

3 Likes

I appreciate all the advice, guys. Unfortunately, I barely have two brain cells to rub together, so a lot of this is over my head. I’ve tried implementing a few different styles of cloning with mixed results. The only one that has come close to working is the Reflection example in the link Lordofduct provided. It did manage to make a deep copy of the class, but also started an avalanche of errors that eventually crashed Unity. It’s a shame this is so complicated. I may just have to find a workaround.

Sounds like you just had code that was going infinite?

In any case, it doesn’t need to be complicated. This is a class that can provide a copy of itself:

public class SomeClass
{
    public int SomeInt;
  
    public SomeClass(int value)
    {
        SomeInt = value;
    }
  
    public SomeClass GetCopy() => new SomeClass(SomeInt);
}

It can be as simple as that.

1 Like

I did finally get some traction on this, and I’m embarrassed to say, it looks a lot like what you guys were telling me to do, it just took me a while to get there. Here’s what I ended up with, for anyone who runs across this in the future. Thanks again for all the help!

public static class DeepCopyUtility
    {
        public static T DeepCopy<T>(T original)
        {
            // Serialize the original object to JSON
            string json = JsonUtility.ToJson(original);
            // Deserialize the JSON back into a new object
            T copy = JsonUtility.FromJson<T>(json);
            return copy;
        }
    }

Implementation

MyClass copy = DeepCopyUtility.DeepCopy(original);