¿Cómo se realiza una copia profunda de un objeto no marcado como serializable (en C #)?

Estoy intentando crear una pila de Portapapeles en C #. Los datos del portapapeles se almacenan en los objetos System.Windows.Forms.DataObject . Quería almacenar cada entrada del portapapeles ( IDataObject ) directamente en una lista genérica. Debido a la forma en que Bitmaps (parece que está) almacenado, creo que necesito realizar una copia profunda primero antes de agregarlo a la lista.

Intenté usar la serialización binaria (ver a continuación) para crear una copia profunda, pero como System.Windows.Forms.DataObject no está marcado como serializable, el paso de serialización falla. ¿Algunas ideas?

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 Respuestas

Escribí el código a continuación para otra pregunta y tal vez podría ser útil para usted en este escenario:

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

Sin embargo, no creo que esta sea la solución final a su problema, aunque puede darle un lugar para comenzar.

3
agregado
Sí, es un error en el código. En lugar de typeof (T) .GetProperties() , debería decir instance.GetType (). GetProperties() . De hecho, yo diría que debería usar los campos en lugar de las propiedades, pero esa es una opción de diseño.
agregado el autor Timwi, fuente
Gracias por tomar una foto de esto (puntos para la convención de nomenclatura de clase también). Desafortunadamente IDataObject no contiene propiedades. Los datos se "extraen" utilizando métodos, de modo que el código anterior devuelve un diccionario vacío.
agregado el autor cgray4, fuente

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

Mi respuesta es mucho mejor aquí que allí, aunque la pregunta anterior termina con:

"... ¿o tal vez una forma diferente de crear un deepclone?"

Una vez hice una inspección a una estructura de objetos a través de Reflection para encontrar todos los ensamblajes necesarios para la deserialización y serializarlos al costado para el arranque.

Con un poco de trabajo uno podría construir un método similar para la copia profunda. Básicamente, necesita un método recursivo que incluya un diccionario para detectar referencias circulares. Dentro del método, inspecciona todos los campos de la siguiente manera:

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)

Sin embargo, esta implementación no es compatible con los controladores de eventos registrados, que también es _ un respaldado por la deserialización. Además, cada objeto en la jerarquía se romperá, si el constructor de su clase necesita inicializar cualquier cosa que no sea configurar todos los campos. El último punto solo funciona con la serialización, si la clase tiene una implementación respectiva, p. método marcado [OnDeserialized] , implementa ISerializable , ....

0
agregado

Busca en los muelles de Serializable y encuentra información sobre los ayudantes de serialización. Puede ajustar el mapa de bits en su propio código de serialización que se integra con .NET Framework.

0
agregado