C # Cloning - работа с несериализуемыми типами данных - PullRequest
4 голосов
/ 12 ноября 2011

Я столкнулся с небольшим камнем преткновения в своем стремлении реализовать клонирование объектов в своем игровом движке.Моя цель - создать систему клонирования, которую мне не нужно поддерживать на уровне классов, если только класс не нуждается в особой обработке.

Настройка моего игрового движка вращается вокруг базового класса Object2D, который содержит некоторыеданные изображения в виде Texture2D.Что ж, долгая история заключается в том, что он содержит DisplayObject, который содержит Sprite, который содержит Texture2D.Естественно, другие классы, например, «Игрок», «Враг», «Снаряд» и т. Д., Все являются производными от базового класса Object2D.

К сожалению, я обнаружил, что класс XNA Texture2D не сериализуем.Это имеет смысл, поскольку мы не хотели бы копировать данные текстуры в памяти все вольно-невольно.

Это создает для меня дилемму.Я использовал методы глубокого клонирования для клонирования объектов, но так как он не сериализуем, я больше не могу этого делать.Я попытался просто пометить Texture2D [NonSerializable], но это вызывает проблемы при попытке рисовать, так как текстура клона нулевая.Мне не удалось найти какой-либо скрытый трюк, который бы позволил мне назначить его после клонирования (например, какой-то метод "onClone ()").

Так что я решил, что сделаю это.Для объектов, которые не могут быть глубоко клонированы в общем, я реализую интерфейс «SpecialClone», который позволяет мне указать метод clone ().

Однако, потому что класс, который не может быть глубоко клонированВ общем, это базовый класс, я вернулся к тому, с чего начал: написание метода клонирования для каждого класса по отдельности.

    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);
        }
    }

С C # я пока не лучший, поэтому яЯ не уверен, что мне не хватает какой-то хитрости, которую я могу использовать, или это действительно то, как мне придется решать эту проблему.Основная цель этого клонирования - для типов Object2D, поэтому было бы очень трудно писать методы клонирования для каждого нового подкласса, который я создаю.

Есть ли способ избежать этого?

Ответы [ 2 ]

1 голос
/ 26 декабря 2011

вы не хотите клонировать Texture2d.

Вы должны рассматривать текстуры (и модели, волновые файлы и т. Д.) как "общий доступ"актив ", это может быть много мегабайт в размере.Клонирование, которое оставило бы вас в мире нехватки памяти, очень быстро (в дополнение к времени, которое требуется для клонирования)

ваше идеальное решение: вместо этого вы хотите клонировать ссылку.

если каждый из ваших экземпляров Object2D не предназначен для сериализации, тогда вы можете просто ссылаться и присваивать одну и ту же Texture2D различным Object2D.Если вы хотите сериализоваться, тогда передайте ссылку на Texture2D, как уже упоминалось, плюс скопируйте строку textureFilePath, которая является сериализованной.

Также обратите внимание, что это то, что ContentManager XNA делает для вас автоматически: если вы запроситеmyPic.xnb "10 раз, он автоматически загрузит его один раз и вернет вам эту ссылку 10 раз.

0 голосов
/ 12 ноября 2011

ICloneable (основной интерфейс .Net) лучше, чем SpecialClone. Вы также можете реализовать интерфейс ISerializable вместе с защищенным конструктором ctor (информация SerializationInfo, контекст StreamingContext).

Это позволит настраивать сериализацию (SerializationInfo - это пакет, в котором может храниться все, что вам нужно для десериализации объекта позже, и который передается вам в защищенном конструкторе).

Так вот и все, что нужно знать о сериализации. Если вы можете сделать это или нет, зависит от того, есть ли что-то в несериализуемом классе (то есть ключ, имя файла, буфер), который вы можете использовать при десериализации для воссоздания этого объекта.

...