Search code examples
c#xnacloneserializablecloning

C# Cloning- working with nonserializable data types


I've hit a bit of a stumbling block in my quest to implement object cloning in my game engine. My goal is to have a cloning system that I don't have to maintain on a class by class basis, unless the class needs special treatment.

My game engine's setup revolves around a base class Object2D, which contains some image data in the form of a Texture2D. Well, the long story is that it contains a DisplayObject, which contains a Sprite, which contains a Texture2D. Naturally, other classes e.g. "Player", "Enemy", "Projectile", etc. all derive from the basic Object2D class.

Unfortunately, I found that XNA's Texture2D class isn't serializable. Which makes sense, as we wouldn't want to be duplicating texture data in memory all willy-nilly.

This creates a dilemma for me. I was using deep cloning methods to clone objects, but since it's not serializable I can no longer do that. I tried just marking the Texture2D [NonSerializable], but this causes problems when I try to draw, as the clone's texture is null. I wasn't able to find any hidden trick that allows me to assign it after cloning (like some sort of an "onClone()" method).

So I figured I'd do this. For objects that can't be deep cloned generically, I implement a "SpecialClone" interface that allows me to specify a clone() method.

However, because the class that can't be deep cloned generically is the base class, I'm right back where I started: writing a clone method on a class by class basis.

    public static T clone<T>(T obj) {
        if (obj == null) return default(T);

        if (!typeof(T).IsSerializable) {
            if (obj is SpecialClone) {
                object obj2 = ((SpecialClone)obj).clone();
                return (T)obj2;
            } else {
                throw new ArgumentException("Object type is not serializable.", "obj type: " + Type.GetTypeHandle(obj));
            }
        } else {
            return deepClone(obj);
        }
    }

    public static T deepClone<T>(T obj) {
        if (obj == null) return default(T);
        if (typeof(T).IsSerializable) {
            try {
                BinaryFormatter bf = new BinaryFormatter();
                MemoryStream ms = new MemoryStream();
                bf.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                T obj2 = (T)bf.Deserialize(ms);
                ms.Dispose();
                return obj2;
            }
            catch (Exception e) {
                Console.WriteLine(e);
                return default(T);
            }
        } else {
            Console.WriteLine(typeof(T) + " is not marked as serializable!");
            return default(T);
        }
    }

I'm not the best with C# yet, so I'm not sure if I'm missing some trick that I can use or if this is really how I'll have to deal with this problem. The main purpose of this cloning is for Object2D types, so it'd be a real pain to have to write clone methods for each new subclass I make.

Is there any way to avoid this?


Solution

  • you do not want to clone the Texture2d.

    You should consider Textures (and models, wave files, etc...) as a "shared asset", it could be many megabytes in size. Cloning that would leave you in a world of out-of-memory hurt real quick (in addition to the time it takes to clone)

    your ideal solution: instead, you want to clone the reference.

    if each of your Object2D instances are not meant to be serialized, then you can just reference-assign the same Texture2D to the various Object2D's. If you want to serialize, then pass the Texture2D reference, as mentioned, plus copy a string textureFilePath, which is what gets serialized.

    Also note that this is what XNA's ContentManager does for you automatically: if you request "myPic.xnb" 10 times, it will automagically load it once and return that reference to you 10 times.