2011-10-31 55 views
5

有序列化相同的對象可能會產生不同的流(假設格式化內置.NET用於兩個序列化的一個),當任何情況呢?可以序列化同一個對象產生不同的流嗎?

這在下面this post的討論上來。聲稱這種情況可能發生,但沒有提供具體的解釋,所以我想知道是否有人可以解釋這個問題?

+0

OK,看來我必須證明這一點。 **我會嘗試製作一個示例應用程序。** – Aliostad

+0

我不認爲是這種情況。流實例可能不同,但內容應該相同。假設他們的狀態在序列化期間沒有被修改。 – Ian

+0

@Ian看我的示例代碼來創建它。 – Aliostad

回答

6

當我在SO問題的意見解釋,這個問題引起的(至少我已經發現的情況下)由字符串輸出的優化。 看來,如果字符串是相同的引用,那麼它會輸出一次。

所以我們的示例代碼它使用一個對象的屬性的長字符串,改變一個字符串的引用,然後連載什麼。然後將流反序列化爲對象(並且這次是因爲字符串被實現,使用相同的引用),然後再次串行化。這一次的流是較小的

OK,這裏是證明代碼:

[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

很好的例子;我覺得有趣的是,它執行引用檢查的麻煩,但沒有特殊的套管字符串相等(因爲它是如此常見的,特別是從數據庫中加載值時)。我嘆了口氣,我的串行器知道檢查字符串不僅僅是參考相等; p –

+0

是啊,protobuf是偉大的,很好的工作。 – Aliostad

+0

我還應該補充說:protobuf也不能保證這一點。好的,實際上它會**輸出同樣的東西,但是當代表一些核心數據類型時(例如**有效代表相同數據的方式),我在谷歌論壇上討論了有關子異常形式的有趣討論有意使用額外的字節) –

1

取決於你所說的「相同」的對象一點。

但是,是的,2個實例可以比較平等=真實,仍然會產生不同的流。

  • 很平凡與equals
  • 的,因爲引起的歸一化或操作的順序細微的差別的損壞或有限的覆蓋。

我驗證了將不同順序添加到字典中的相同元素會產生不同的流。我假設你會考慮2個相同內容的字典「平等」。

+0

編寫不好的序列化程序也許能夠做到這一點?設想一個輸出隨機數據的序列化器,該序列化器在反序列化期間被忽略的字段中。 – mah

+1

@mah:可能但不太可能。但是一個序列化器通常會生成ID,沒有必要以可重複的方式這樣做。 –

+0

@mah .NET內置格式化程序的行爲是否像這樣遠程?我知道如果你自己編寫的話,你可以做各種奇怪的東西,但那些與.NET來的呢? –

3

一些核心類型(DateTime - 特別指出「厚道」,或Decimal注意規模)可以有一個報告爲相等的值,但不同的序列;例如:

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

至於確切同一個對象是否可以序列不同的 - 嗯,這通常不是一個保證序列化可以對任何要求。序列化是關於存儲和檢索您的數據/對象 - 而不是關於證明平等。例如,xml具有各種空白和命名空間標準化問題,使其不適合比較。 BinaryFormatter和其他二進制序列化器看起來更有誘惑力,但我不確定它們是否保證您正在尋找的行爲。

我不會真的相信通過這種方法進行比較,除非串行器明確作出了這種保證。

+0

謝謝馬克。我也放了一些顯示字符串行爲的東西。 – Aliostad

相關問題