2010-06-01 10 views
8

Des Versuch, einen Stream zu List<T> (oder jeder anderen Art) deserialisieren und mit dem Fehler sind Fehler:Deserialize Stream Liste <T> oder jede andere Art

The type arguments for method Foo.Deserialize<T>(System.IO.Stream) cannot be inferred from the usage. Try specifying the type arguments explicitly.

Dies schlägt fehl:

public static T Deserialize<T>(this Stream stream) 
{ 
    BinaryFormatter bin = new BinaryFormatter(); 
    return (T)bin.Deserialize(stream); 
} 

Aber dies funktioniert:

public static List<MyClass.MyStruct> Deserialize(this Stream stream) 
{ 
    BinaryFormatter bin = new BinaryFormatter(); 
    return (List<MyClass.MyStruct>)bin.Deserialize(stream); 
} 

oder:

public static object Deserialize(this Stream stream) 
{ 
    BinaryFormatter bin = new BinaryFormatter(); 
    return bin.Deserialize(stream); 
} 

Ist es möglich, dies ohne Gießen zu tun, z.B. (List<MyStruct>)stream.Deserialize()?

Update:
Mit stream.Deserialize<List<MyClass.MyStruct>>() führt zu einem Fehler:

System.InvalidCastException: Unable to cast object of type 'System.RuntimeType' 
to type 'System.Collections.Generic.List`1[MyClass+MyStruct]'. 
at StreamExtensions.Deserialize[T](Stream stream) 
at MyClass.RunSnippet() 

Update 2 (Probe Konsole app) - einmal ausführen, um die Datei zu erstellen, wieder von ihm zu lesen

using System; 
using System.IO; 
using System.Collections.Generic; 
using System.Runtime.Serialization; 
using System.Runtime.Serialization.Formatters; 
using System.Runtime.Serialization.Formatters.Binary; 

public static class StreamExtensions 
{ 
    public static Stream Serialize<T>(this T o) where T : new() 
    { 
     Stream stream = new MemoryStream(); 
     BinaryFormatter bin = new BinaryFormatter(); 
     bin.Serialize(stream, typeof(T)); 
     return stream; 
    } 

    public static T Deserialize<T>(this Stream stream) where T : new() 
    { 
     BinaryFormatter bin = new BinaryFormatter(); 
     return (T)bin.Deserialize(stream); 
    } 

    public static void WriteTo(this Stream source, Stream destination) 
    { 
     byte[] buffer = new byte[32768]; 
     source.Position = 0; 
     if(source.Length < buffer.Length) buffer = new byte[source.Length]; 
     int read = 0; 
     while ((read = source.Read(buffer, 0, buffer.Length)) != 0) 
     { 
      destination.Write(buffer, 0, read); 
     } 
    } 
} 


public class MyClass 
{ 
    public struct MyStruct 
    { 
     public string StringData; 
     public MyStruct(string stringData) 
     { 
      this.StringData = stringData; 
     } 
    } 

    public static void Main() 
    { 
     // binary serialization 
     string filename_bin = "mydata.bin"; 
     List<MyStruct> l; 
     if(!File.Exists(filename_bin)) 
     { 
      Console.WriteLine("Serializing to disk"); 
      l = new List<MyStruct>(); 
      l.Add(new MyStruct("Hello")); 
      l.Add(new MyStruct("Goodbye")); 
      using (Stream stream = File.Open(filename_bin, FileMode.Create)) 
      { 
       Stream s = l.Serialize(); 
       s.WriteTo(stream); 
      } 
     } 
     else 
     { 
      Console.WriteLine("Deserializing from disk"); 
      try 
      { 
       using (Stream stream = File.Open(filename_bin, FileMode.Open)) 
       { 
        l = stream.Deserialize<List<MyStruct>>(); 
       } 
      } 
      catch(Exception ex) 
      { 
       l = new List<MyStruct>(); 
       Console.WriteLine(ex.ToString()); 
      } 
     } 

     foreach(MyStruct s in l) 
     { 
      Console.WriteLine(
       string.Format("StringData: {0}", 
        s.StringData 
       ) 
      ); 
     } 

     Console.ReadLine(); 
    } 
} 

Antwort

8

Ich nehme an, dass Sie Ihre Erweiterung Methode wie folgt aufrufen:

List<MyStruct> result = mystream.Deserialize();  

In diesem Fall kann der Compiler die T für Deserialize nicht ermitteln (es sieht nicht auf die Variable, der das Methodenaufrufergebnis zugewiesen ist).

So müssen Sie die Art Argument explizit angeben:

List<MyStruct> result = mystream.Deserialize<List<MyStruct>>(); 

Dies funktioniert:

public static class StreamExtensions 
{ 
    public static void SerializeTo<T>(this T o, Stream stream) 
    { 
     new BinaryFormatter().Serialize(stream, o); // serialize o not typeof(T) 
    } 

    public static T Deserialize<T>(this Stream stream) 
    { 
     return (T)new BinaryFormatter().Deserialize(stream); 
    } 
} 

[Serializable] // mark type as serializable 
public struct MyStruct 
{ 
    public string StringData; 
    public MyStruct(string stringData) 
    { 
     this.StringData = stringData; 
    } 
} 

public static void Main() 
{ 
    MemoryStream stream = new MemoryStream(); 

    new List<MyStruct> { new MyStruct("Hello") }.SerializeTo(stream); 

    stream.Position = 0; 

    var mylist = stream.Deserialize<List<MyStruct>>(); // specify type argument 
} 
+3

@Sam: Es gibt einen Fehler in Ihrer Serialize-Methode. Sie serialisieren 'typeof (T)' (was ein RuntimeType ist) anstelle des Objekts 'o'! – dtb

+0

Had [Serializable], habe nur vergessen, es in die Probe zu legen ... Bug war die Serialisierung und nicht die Deserialisierung (habe nicht daran gedacht, weil keine Ausnahme geworfen wurde) – SamWM

+0

@ dtb: Ich hatte genau das gleiche Problem mit mein BinaryFormatter, danke, dass du es aufgezeigt hast. Es ist leicht zu übersehen, da XmlSerializer typeof() im Konstruktor verwendet. – angularsen

1

Sie Ihre ursprüngliche generische Methode verwenden können, müssen Sie nur den generischen Typen angeben ausdrücklich wie so ...

stream.Deserialize<List<MyClass.MyStruct>>(); 
1

Sie serialisieren den Typ der Liste und nicht die tatsächliche Liste. Es sollte sein:

bin.Serialize(stream, o) 

Außerdem werden Sie MyStruct als Serializable markieren müssen es es richtig zu serialisiert.

Verwandte Themen