2011-10-31 12 views
5

¿Hay alguna situación en la que la serialización de un mismo objeto podría producir diferentes corrientes (suponiendo uno de los formateadores integrado .NET se utiliza tanto para serializaciones)?¿Puede serializar el mismo objeto producir diferentes flujos?

Esto surgió en la discusión que sigue this post. Se hizo la afirmación de que esto puede suceder, pero no se ofreció ninguna explicación concreta, por lo que me preguntaba si alguien puede arrojar algo de luz sobre el tema.

+0

bien, parece que tengo que probarlo. ** Trataré de hacer una aplicación de muestra. ** – Aliostad

+0

No creo que sea el caso. Las instancias de flujo pueden ser diferentes, pero el contenido debe ser el mismo. Suponiendo que su estado no se modifique durante la serialización. – Ian

+0

@Ian ver mi código de muestra para crearlo. – Aliostad

Respuesta

6

Como he explicado en el comentario de que la cuestión de forma, se produce el problema (al menos el caso he descubierto) por la optimización de la cadena de salida. Parece que si las cadenas son la misma referencia, entonces se emitirá una vez.

Así que lo que el código de ejemplo lo hace para utilizar una cadena larga para las propiedades de un objeto y cambiar la referencia de una cadena y luego serializar. A continuación, deserialice la transmisión de nuevo a objeto (y esta vez ya que la cadena está intervenida, se utiliza la misma referencia) y luego vuelva a serializar. Esta vez, la secuencia es más pequeña.

OK, aquí es el código de la prueba:

[Serializable] 
public class Proof 
{ 
    public string S1 { get; set; } 
    public string S2 { get; set; } 
    public string S3 { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 

     const string LongString = 
      "A value that is going to change the world nad iasjdsioajdsadj sai sioadj sioadj siopajsa iopsja iosadio jsadiojasd "; 

     var proof = new Proof() { 
      S1 = LongString, 
      S2 = LongString, 
      S3 = LongString 
     }; 

     proof.S2 = LongString.Substring(0, 10) + LongString.Substring(10); // just add up first 10 character with the rest. 
       //This just makes sure reference is not the same although values will be 

     Console.WriteLine(proof.S1 == proof.S2); 
     Console.WriteLine(proof.S1 == proof.S3); 
     Console.WriteLine(proof.S2 == proof.S3); 
     Console.WriteLine("So the values are all the same..."); 

     BinaryFormatter bf = new BinaryFormatter(); 
     MemoryStream stream = new MemoryStream(); 
     bf.Serialize(stream, proof); 
     byte[] buffer = stream.ToArray(); 
     Console.WriteLine("buffer length is " + buffer.Length); // outputs 449 on my machine 
     stream.Position = 0; 
     var deserProof = (Proof) bf.Deserialize(new MemoryStream(buffer)); 
     deserProof.S1 = deserProof.S2; 
     deserProof.S3 = deserProof.S2; 
     MemoryStream stream2 = new MemoryStream(); 
     new BinaryFormatter().Serialize(stream2, deserProof); 

     Console.WriteLine("buffer length now is " + stream2.ToArray().Length); // outputs 333 on my machine!! 
     Console.WriteLine("What? I cannot believe my eyes! Someone help me ........"); 

     Console.Read(); 
    } 
+0

buen ejemplo; Me parece interesante que se tomó la molestia de hacer una verificación de referencia, pero sin la igualdad de cadenas especiales (ya que es muy común, especialmente cuando se cargan valores de un ejemplo de base de datos). Suspiro un suspiro feliz que * mi * serializador sabe para comprobar las cadenas para algo más que la igualdad de referencia; p –

+0

Sí, compañero, protobuf es genial, buen trabajo. – Aliostad

+0

También debería agregar: protobuf tampoco garantiza esto. OK, en realidad, ** generará ** la misma cosa, pero tuve una discusión interesante en el foro de google sobre formas subnormales cuando represento algunos de los tipos de datos centrales (es decir, formas de representar ** válidamente ** los mismos datos) intencionalmente utilizando bytes adicionales) –

1

depende un poco de lo que se llama el 'mismo' objeto.

Pero sí, 2 casos podría comparar como igual = true y todavía producir diferentes corrientes.

  • muy trivial con un override roto o limitada de Iguales
  • debido a las diferencias sutiles causadas por normalizaciones o el orden de las operaciones.

Comprobé que agregar los mismos elementos a un diccionario en un orden diferente produce una secuencia diferente. Supongo que consideraría 2 diccionarios con el mismo contenido 'igual'.

+0

¿Un serializador mal escrito puede hacer esto también?Imagine un serializador que genera datos aleatorios, en un campo que se ignora durante la deserialización. – mah

+1

@mah: posible pero poco probable. Pero un serializador a menudo genera identificadores, no hay razón para hacerlo de una manera repetible. –

+0

@mah ¿Alguno de los formateadores incorporados de .NET se comporta de forma remota de esta manera? Soy consciente de que podrías hacer todo tipo de cosas raras si las escribes tú mismo, pero ¿qué hay de las que vienen con .NET? –

3

Algunos tipos de núcleo (DateTime - Observando en particular la "clase", o Decimal tomando nota de la escala) podría tener valores que informan como igual, pero que serializan diferente; por ejemplo:

using System; 
using System.IO; 
using System.Runtime.Serialization.Formatters.Binary; 
[Serializable] 
class Foo 
{ 
    public decimal Bar { get; set; } 
    static void Main() 
    { 
     Foo foo = new Foo(); 
     foo.Bar = 123.45M; 
     var s = Serialize(foo); 

     Foo foo2 = new Foo(); 
     foo2.Bar = 123.4500M; 

     bool areSame = foo.Bar == foo2.Bar; // true 
     var s2 = Serialize(foo2); 

     bool areSerializedTheSame = s == s2; // false 
    } 
    static string Serialize(object obj) 
    { 
     using (var ms = new MemoryStream()) 
     { 
      new BinaryFormatter().Serialize(ms, obj); 
      return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length); 
     } 
    } 
} 

En cuanto a si la exacta mismo objeto podría serializar diferente - bien, eso no es por lo general una garantía de que la serialización hace ninguna afirmación acerca. Serialización se trata de almacenar y recuperar sus datos/objetos - no trata de probar la igualdad. Por ejemplo, xml tiene todo tipo de problemas de espacio en blanco y espacio de nombres que lo hacen inadecuado para la comparación. BinaryFormatter y otros serializadores binarios parecen más tentadores, pero no estoy seguro de que garanticen el comportamiento que está buscando.

Realmente no confiaría en una comparación hecha a través de tal enfoque a menos que el serializador explícitamente hiciera esta garantía.

+0

Gracias Marc. También he puesto algo que muestra el comportamiento con respecto a las cadenas. – Aliostad

Cuestiones relacionadas