Como executar uma cópia profunda de um objeto não marcado como serializável (em c #)?

Eu estou tentando criar uma pilha de área de transferência em c #. Os dados da área de transferência são armazenados em objetos System.Windows.Forms.DataObject . Eu queria armazenar cada entrada da área de transferência ( IDataObject ) diretamente em uma lista genérica. Devido ao modo como os Bitmaps (parecem estar) armazenados, estou pensando que preciso executar uma cópia profunda antes de adicioná-la à lista.

Eu tentei usar a serialização binária (veja abaixo) para criar uma cópia profunda, mas como System.Windows.Forms.DataObject não está marcada como serializável, a etapa de serialização falha. Alguma ideia?

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 Respostas

Eu escrevi o código abaixo para outra pergunta e talvez possa ser útil para você neste cenário:

    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 instance, IDictionary 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(property.SetValue), 
                                            kvp.Value
                                    }
                    );

                    foreach (var match in matches)
                            match.Set(instance, match.Value, null);
            }
            public static IDictionary Serialize(T instance) { var props = typeof(T).GetProperties(); var ret = new Dictionary(); foreach (var property in props) { if (!property.CanWrite || !property.CanRead) continue; ret.Add(property.Name, property.GetValue(instance, null)); } return ret; } } 

No entanto, eu não acho que esta será a solução final para o seu problema, embora possa lhe dar um lugar para começar.

3
adicionado
Sim, é um bug no código. Em vez de typeof (T) .GetProperties() , ele deve dizer instance.GetType (). GetProperties() . Na verdade, eu diria que deveria usar os campos em vez de propriedades, mas isso é uma escolha de design.
adicionado o autor Timwi, fonte
Obrigado por dar uma olhada nisso (pontos para a convenção de nomenclatura de classes também). Infelizmente IDataObject não contém propriedades. Os dados são "extraídos" usando métodos, de modo que o código acima retorna um dicionário vazio.
adicionado o autor cgray4, fonte

Copy of my answer to: difference between DataContract attribute and Serializable attribute in .net

Minha resposta se encaixa muito melhor aqui do que lá, embora a pergunta acima termine com:

"... ou talvez uma maneira diferente de criar um deepclone?"

Uma vez fiz uma inspeção em uma estrutura de objetos via Reflection para encontrar todos os assemblies necessários para desserialização e serializá-los ao lado para bootstrapping.

Com um pouco de trabalho, pode-se construir um método semelhante para cópia profunda. Basicamente você precisa de um método recursivo que carregue ao longo de um dicionário para detectar referências circulares. Dentro do método você inspeciona todos os campos assim:

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

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

To get it working you need to add an output object and something like System.Runtime.Serialization.FormatterServices.GetUninitializedObject(Type type) to create the most shallowest copy (even without copying references) of each field's value. Finally you can set each field with something like field.SetValue(input, output)

No entanto, esta implementação não suporta manipuladores de eventos registrados, que é _ un _supported por desserialização também. Além disso, cada objeto na hierarquia será quebrado, se o construtor de sua classe precisar inicializar qualquer coisa, mas configurando todos os campos. O último ponto só funciona com serialização, se a classe tiver uma implementação respectiva, por ex. método marcado [OnDeserialized] , implementa ISerializable , ....

0
adicionado

Procure as docas para Serializable e encontre o material sobre os auxiliares de serialização. Você pode envolver o bitmap em seu próprio código de serialização, que se integra à estrutura .net.

0
adicionado