Ich überprüfe, ob protobuf-net ein Ersatz für DataContracts sein kann. Neben der ausgezeichneten Leistung ist es wirklich eine ordentliche Bibliothek. Das einzige Problem, das ich habe, ist, dass die .NET-Serialisierer keine Annahmen darüber treffen, was sie gerade de/serialisieren. Besonders Objekte, die einen Verweis auf das getippte Objekt enthalten, sind ein Problem.
[DataMember(Order = 3)]
public object Tag1 // Der DataContract enthielt ein Objekt, das jetzt zu einem SimulatedObject wird
{
get;
set;
}
Ich habe versucht, ein Objekt mit Protocol Buffers nachzubilden, mit einem kleinen generischen Helfer, der für jeden möglichen Typ in einem separaten stark typisierten Feld speichert.
Ist dies ein empfohlener Ansatz, um mit Feldern umzugehen, die in eine Reihe von unterschiedlichen nicht verwandten Typen de/serialisieren?
Im Folgenden finden Sie den Beispielcode für ein SimulatedObject, das bis zu 10 verschiedene Typen speichern kann.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using ProtoBuf;
using System.Diagnostics;
[DataContract]
public class SimulatedObject
{
[DataMember(Order = 20)]
byte FieldHasValue; // Die Nummer zeigt an, welches Feld tatsächlich einen Wert hat
[DataMember(Order = 1)]
T1 I1;
[DataMember(Order = 2)]
T2 I2;
[DataMember(Order = 3)]
T3 I3;
[DataMember(Order = 4)]
T4 I4;
[DataMember(Order = 5)]
T5 I5;
[DataMember(Order = 6)]
T6 I6;
[DataMember(Order = 7)]
T7 I7;
[DataMember(Order = 8)]
T8 I8;
[DataMember(Order = 9)]
T9 I9;
[DataMember(Order = 10)]
T10 I10;
public object Data
{
get
{
switch(FieldHasValue)
{
case 0: return null;
case 1: return I1;
case 2: return I2;
case 3: return I3;
case 4: return I4;
case 5: return I5;
case 6: return I6;
case 7: return I7;
case 8: return I8;
case 9: return I9;
case 10: return I10;
default:
throw new NotSupportedException(String.Format("Das Feld FieldHasValue hat einen ungültigen Wert {0}. Dies deutet auf beschädigte Daten oder inkompatible Datenlayoutänderungen hin", FieldHasValue));
}
}
set
{
I1 = default(T1);
I2 = default(T2);
I3 = default(T3);
I4 = default(T4);
I5 = default(T5);
I6 = default(T6);
I7 = default(T7);
I8 = default(T8);
I9 = default(T9);
I10 = default(T10);
if (value != null)
{
Type t = value.GetType();
if (t == typeof(T1))
{
FieldHasValue = 1;
I1 = (T1) value;
}
else if (t == typeof(T2))
{
FieldHasValue = 2;
I2 = (T2) value;
}
else if (t == typeof(T3))
{
FieldHasValue = 3;
I3 = (T3) value;
}
else if (t == typeof(T4))
{
FieldHasValue = 4;
I4 = (T4) value;
}
else if (t == typeof(T5))
{
FieldHasValue = 5;
I5 = (T5) value;
}
else if (t == typeof(T6))
{
FieldHasValue = 6;
I6 = (T6) value;
}
else if (t == typeof(T7))
{
FieldHasValue = 7;
I7 = (T7) value;
}
else if (t == typeof(T8))
{
FieldHasValue = 8;
I8 = (T8) value;
}
else if (t == typeof(T9))
{
FieldHasValue = 9;
I9 = (T9) value;
}
else if (t == typeof(T10))
{
FieldHasValue = 10;
I10 = (T10) value;
}
else
{
throw new NotSupportedException(String.Format("Der Typ {0} wird nicht für die Serialisierung unterstützt. Fügen Sie bitte den Typ zur Liste der generischen Argumente von SimulatedObject hinzu.", t.FullName));
}
}
}
}
}
[DataContract]
class Customer
{
/*
[DataMember(Order = 3)]
public object Tag1 // Der DataContract enthielt ein Objekt, das jetzt zu einem SimulatedObject wird
{
get;
set;
}
*/
[DataMember(Order = 3)]
public SimulatedObject Tag1 // Kann bis zu 10 verschiedene Typen enthalten
{
get;
set;
}
[DataMember(Order = 4)]
public List Strings
{
get;
set;
}
}
[DataContract]
public class Other
{
[DataMember(Order = 1)]
public string OtherData
{
get;
set;
}
}
[DataContract]
public class SomethingDifferent
{
[DataMember(Order = 1)]
public string OtherData
{
get;
set;
}
}
class Program
{
static void Main(string[] args)
{
Customer c = new Customer
{
Strings = new List { "Erste", "Zweite", "Dritte" },
Tag1 = new SimulatedObject
{
Data = new Other { OtherData = "Stringwert "}
}
};
const int Läufe = 1000 * 1000;
var stream = new MemoryStream();
var sw = Stopwatch.StartNew();
Serializer.Serialize(stream, c);
sw = Stopwatch.StartNew();
for (int i = 0; i < Läufe; i++)
{
stream.Position = 0;
stream.SetLength(0);
Serializer.Serialize(stream, c);
}
sw.Stop();
Console.WriteLine("Datengröße mit dem Protocol Buffer-Serializer: {0}, {1} Objekte dauerten {2}s", stream.ToArray().Length, Läufe, sw.Elapsed.TotalSeconds);
stream.Position = 0;
var newCustw = Serializer.Deserialize(stream);
sw = Stopwatch.StartNew();
for (int i = 0; i < Läufe; i++)
{
stream.Position = 0;
var newCust = Serializer.Deserialize(stream);
}
sw.Stop();
Console.WriteLine("Objekt mit dem Protocol Buffer-Deserializer lesen: {0} Objekte dauerten {1}s", Läufe, sw.Elapsed.TotalSeconds);
}
}