Как выполнить глубокую копию объекта, не помеченного как сериализуемый (в C #)? - PullRequest
4 голосов
/ 14 октября 2008

Я пытаюсь создать стек буфера обмена в C #. Данные буфера обмена хранятся в System.Windows.Forms.DataObject объектах. Я хотел сохранить каждую запись в буфере обмена (IDataObject) непосредственно в общем списке. Из-за способа хранения растровых изображений (кажется, они хранятся) я думаю, что мне нужно сначала выполнить глубокое копирование, прежде чем я добавлю его в список.

Я попытался использовать двоичную сериализацию (см. Ниже) для создания глубокой копии, но, поскольку System.Windows.Forms.DataObject не помечен как сериализуемая, этап сериализации завершается неудачно. Есть идеи?

public IDataObject GetClipboardData()
{
    MemoryStream memoryStream = new MemoryStream();
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(memoryStream, Clipboard.GetDataObject());
    memoryStream.Position = 0;
    return (IDataObject) binaryFormatter.Deserialize(memoryStream);
}

Ответы [ 3 ]

3 голосов
/ 14 октября 2008

Я написал код ниже для другого вопроса, и, возможно, он пригодится вам в этом сценарии:

    public static class GhettoSerializer
    {
            // you could make this a factory method if your type
            // has a constructor that appeals to you (i.e. default 
            // parameterless constructor)
            public static void Initialize<T>(T instance, IDictionary<string, object> values)
            {
                    var props = typeof(T).GetProperties();

                    // my approach does nothing to handle rare properties with array indexers
                    var matches = props.Join(
                            values,
                            pi => pi.Name,
                            kvp => kvp.Key,
                            (property, kvp) =>
                                    new {
                                            Set = new Action<object,object,object[]>(property.SetValue), 
                                            kvp.Value
                                    }
                    );

                    foreach (var match in matches)
                            match.Set(instance, match.Value, null);
            }
            public static IDictionary<string, object> Serialize<T>(T instance)
            {
                    var props = typeof(T).GetProperties();

                    var ret = new Dictionary<string, object>();

                    foreach (var property in props)
                    {
                            if (!property.CanWrite || !property.CanRead)
                                    continue;
                            ret.Add(property.Name, property.GetValue(instance, null));
                    }

                    return ret;
            }
    }

Однако я не думаю, что это будет окончательным решением вашей проблемы, хотя оно может дать вам место для начала.

0 голосов
/ 07 ноября 2013

Копия моего ответа: разница между атрибутом DataContract и атрибутом Serializable в .net

Мой ответ здесь гораздо лучше, чем там, хотя вопрос выше заканчивается на:

"... или, может быть, другой способ создания глубокого клона?"

Однажды я сделал некоторую проверку структуры объекта через Reflection, чтобы найти все сборки, необходимые для десериализации, и сериализовать их вместе для начальной загрузки.

Немного потрудившись, можно создать аналогичный метод для глубокого копирования. По сути, вам нужен рекурсивный метод, который ведет словарь для обнаружения циклических ссылок. Внутри метода вы проверяете все поля примерно так:

private void InspectRecursively(object input,
    Dictionary<object, bool> processedObjects)
{
  if ((input != null) && !processedObjects.ContainsKey(input))
  {
    processedObjects.Add(input, true);

    List<FieldInfo> fields = type.GetFields(BindingFlags.Instance |
        BindingFlags.Public | BindingFlags.NonPublic );
    foreach (FieldInfo field in fields)
    {
      object nextInput = field.GetValue(input);

      if (nextInput is System.Collections.IEnumerable)
      {
        System.Collections.IEnumerator enumerator = (nextInput as
            System.Collections.IEnumerable).GetEnumerator();

        while (enumerator.MoveNext())
        {
          InspectRecursively(enumerator.Current, processedObjects);
        }
      }
      else
      {
        InspectRecursively(nextInput, processedObjects);
      }
    }
  }
}

Чтобы это работало, вам нужно добавить выходной объект и что-то вроде System.Runtime.Serialization.FormatterServices.GetUninitializedObject(Type type), чтобы создать самую мелкую копию (даже без копирования ссылок) значения каждого поля. Наконец, вы можете установить для каждого поля что-то вроде field.SetValue(input, output)

Однако эта реализация не поддерживает зарегистрированные обработчики событий, что также поддерживается десериализацией _ un _. Кроме того, каждый объект в иерархии будет нарушен, если конструктор его класса должен что-либо инициализировать, кроме установки всех полей. Последний пункт работает только с сериализацией, если класс имеет соответствующую реализацию, например, метод, помеченный [OnDeserialized], реализует ISerializable, ....

0 голосов
/ 05 января 2009

Посмотрите доки для Serializable и найдите материал о помощниках сериализации. Вы можете обернуть растровое изображение в свой собственный код сериализации, который интегрируется с .net framework.

...