Но не знаю как сделать поддержку циклов в графе объекта.
Для .NET 2.0.
Используется Dynamic Method.
Прирост в скорости при десериализации приблизительно в 5 раз, при сериализации в 2 раза.
Вот результаты теста:
Здравствуйте, Chardex, Вы писали:
C>Но не знаю как сделать поддержку циклов в графе объекта.
Так же, как это сделано в стандартном сериализаторе — при сериализации завести словарь, где ключ — объект, а значение — некий идентификатор. Если при сериализации встречается объект, который уже есть в словаре, то вместо его состояния записать вот этот вот идентификатор. При чтении то же самое, но наоборот.
Re[2]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Oyster, Вы писали:
O>Так же, как это сделано в стандартном сериализаторе — при сериализации завести словарь, где ключ — объект, а значение — некий идентификатор. Если при сериализации встречается объект, который уже есть в словаре, то вместо его состояния записать вот этот вот идентификатор. При чтении то же самое, но наоборот.
Я так думал, но мне кажется что это скажется на производительность, хотя попробую.
Re[3]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Если кому интересно — могу добавить комментариев CustomFormatter.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace Chardex
{
public class CustomFormatter
{
private static Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();
private static Dictionary<int, TypeHelper> _processedTypes = new Dictionary<int, TypeHelper>();
private static Dictionary<Type, Tuple<string, long>> _typeInfos = new Dictionary<Type, Tuple<string, long>>();
public void Serialize(Stream stream, object graph)
{
Serialize(graph, new SerializationContext(stream));
}
private void Serialize(object obj, SerializationContext context)
{
if (obj == null)
{
context.Write(Codes.Null);
return;
}
Type objType = obj.GetType();
if (objType == typeof(Int32))
{
context.Write(Codes.Int32);
context.Write((Int32)obj);
return;
}
if (objType == typeof(Int16))
{
context.Write(Codes.Int16);
context.Write((Int16)obj);
return;
}
if (objType == typeof(Int64))
{
context.Write(Codes.Int64);
context.Write((Int64)obj);
return;
}
if (objType == typeof(UInt32))
{
context.Write(Codes.UInt32);
context.Write((UInt32)obj);
return;
}
if (objType == typeof(UInt16))
{
context.Write(Codes.UInt16);
context.Write((UInt16)obj);
return;
}
if (objType == typeof(UInt64))
{
context.Write(Codes.UInt64);
context.Write((UInt64)obj);
return;
}
if (objType == typeof(Boolean))
{
context.Write(Codes.Boolean);
context.Write((Boolean)obj);
return;
}
if (objType == typeof(String))
{
context.Write(Codes.String);
context.Write((String)obj);
return;
}
if (objType == typeof(Byte))
{
context.Write(Codes.Byte);
context.Write((Byte)obj);
return;
}
if (objType == typeof(Double))
{
context.Write(Codes.Double);
context.Write((Double)obj);
return;
}
if (objType == typeof(Single))
{
context.Write(Codes.Single);
context.Write((Single)obj);
return;
}
if (objType == typeof(Decimal))
{
context.Write(Codes.Decimal);
context.Write((Decimal)obj);
return;
}
if (objType.IsPrimitive)
throw new ArgumentException("Primitiv type which is not exepted", "obj");
if (context.AddedObjects.Contains(obj))
{
context.Write(Codes.Reference);
context.Write(context.AddedObjects.IndexOf(obj));
return;
}
context.AddedObjects.Add(obj);
if (objType.IsArray)
{
WriteArray(obj, objType, context);
return;
}
if (typeof(IDictionary).IsAssignableFrom(objType))
{
WriteDictionary(obj, objType, context);
return;
}
if (typeof(IList).IsAssignableFrom(objType))
{
WriteList(obj, objType, context);
return;
}
WriteCustomObject(obj, objType, context);
}
private object Deserialize(DeserializationContext context)
{
byte code = context.ReadByte();
if (code == Codes.Null)
return null;
if (code == Codes.Int32)
return context.ReadInt32();
if (code == Codes.Int16)
return context.ReadInt16();
if (code == Codes.Int64)
return context.ReadInt64();
if (code == Codes.String)
return context.ReadString();
if (code == Codes.Byte)
return context.ReadByte();
if (code == Codes.Boolean)
return context.ReadBoolean();
if (code == Codes.Array)
return ReadArray(context);
if (code == Codes.List)
return ReadList(context);
if (code == Codes.Dictionaty)
return ReadDictionary(context);
if (code == Codes.CustomObject)
return ReadCustomObject(context);
if (code == Codes.UInt32)
return context.ReadUInt32();
if (code == Codes.UInt16)
return context.ReadUInt16();
if (code == Codes.UInt64)
return context.ReadUInt64();
if (code == Codes.Decimal)
return context.ReadDecimal();
if (code == Codes.Double)
return context.ReadDouble();
if (code == Codes.Single)
return context.ReadSingle();
if (code == Codes.Reference)
return context.AddedObjects[context.ReadInt32()];
throw new ArgumentException("Unknown code " + code);
}
public object Deserialize(Stream stream)
{
return Deserialize(new DeserializationContext(stream));
}
#region Read methods
private object ReadCustomObject(DeserializationContext context)
{
TypeHelper typeHelper = ReadType(context);
long pos = context.Stream.Position;
int length = context.ReadInt32();
if (typeHelper == null)
{
context.Stream.Position = pos + length;
return null;
}
long endPos = context.Stream.Position + length - sizeof(int);
object obj = typeHelper.CreateInstance();
context.AddedObjects.Add(obj);
if (typeHelper.HashChanged)
{
try
{
if (typeHelper.IsValueType)
{
List<Tuple<int, ValueTypeSetterDelegate>> valueTypeSetters = typeHelper.ValueTypeSetters;
int index = 0;
while (context.Stream.Position != endPos)
{
int fieldKey = context.ReadInt32();
object field = Deserialize(context);
if (valueTypeSetters[index].ItemA == fieldKey)
obj = valueTypeSetters[index].ItemB(obj, field);
else
{
for (int i = 0; i < valueTypeSetters.Count; i++)
{
if (valueTypeSetters[i].ItemA == fieldKey)
{
obj = valueTypeSetters[i].ItemB(obj, field);
break;
}
}
}
index++;
}
}
else
{
List<Tuple<int, ReferenceTypeSetterDelegate>> referenceTypeSetters = typeHelper.ReferenceTypeSetters;
int index = 0;
while (context.Stream.Position != endPos)
{
int fieldKey = context.ReadInt32();
object field = Deserialize(context);
if (referenceTypeSetters[index].ItemA == fieldKey)
referenceTypeSetters[index].ItemB(obj, field);
else
{
for (int i = 0; i < referenceTypeSetters.Count; i++)
{
if (referenceTypeSetters[i].ItemA == fieldKey)
{
referenceTypeSetters[i].ItemB(obj, field);
break;
}
}
}
index++;
}
}
}
catch
{
context.Stream.Position = pos + length;
}
}
else
{
if (typeHelper.IsValueType)
{
List<Tuple<int, ValueTypeSetterDelegate>> setters = typeHelper.ValueTypeSetters;
int index = 0;
while (context.Stream.Position != endPos)
{
context.ReadInt32();
object field = Deserialize(context);
obj = setters[index].ItemB(obj, field);
index++;
}
}
else
{
List<Tuple<int, ReferenceTypeSetterDelegate>> setters = typeHelper.ReferenceTypeSetters;
int index = 0;
while (context.Stream.Position != endPos)
{
context.ReadInt32();
object field = Deserialize(context);
setters[index].ItemB(obj, field);
index++;
}
}
}
return obj;
}
private object ReadArray(DeserializationContext context)
{
TypeHelper typeHelper = ReadType(context);
long pos = context.Stream.Position;
int msLength = context.ReadInt32();
if (typeHelper == null || typeHelper.Type == null)
{
context.Stream.Position = pos + msLength;
return null;
}
int length = context.ReadInt32();
Array arr = Array.CreateInstance(typeHelper.Type, length);
context.AddedObjects.Add(arr);
for (int i = 0; i < length; i++)
{
arr.SetValue(Deserialize(context), i);
}
return arr;
}
private object ReadDictionary(DeserializationContext context)
{
TypeHelper typeHelper = ReadType(context);
long pos = context.Stream.Position;
int msLength = context.ReadInt32();
if (typeHelper == null)
{
context.Stream.Position = pos + msLength;
return null;
}
int length = context.ReadInt32();
IDictionary dic = (IDictionary)typeHelper.CreateInstance();
context.AddedObjects.Add(dic);
for (int i = 0; i < length; i++)
{
object key = Deserialize(context);
object val = Deserialize(context);
dic.Add(key, val);
}
return dic;
}
private object ReadList(DeserializationContext context)
{
TypeHelper typeHelper = ReadType(context);
long pos = context.Stream.Position;
int msLength = context.ReadInt32();
if (typeHelper == null)
{
context.Stream.Position = pos + msLength;
return null;
}
int length = context.ReadInt32();
IList collection = (IList)typeHelper.CreateInstance();
context.AddedObjects.Add(collection);
for (int i = 0; i < length; i++)
{
collection.Add(Deserialize(context));
}
return collection;
}
private TypeHelper ReadType(DeserializationContext context)
{
int typeCode = context.ReadInt32();
TypeHelper typeHelper;
if (typeCode == 0)
{
string typeStr = context.ReadString();
long key = context.ReadInt64();
if (!_processedTypes.TryGetValue(typeCode, out typeHelper))
{
Type type;
if (!_typeCache.TryGetValue(typeStr, out type))
{
type = Type.GetType(typeStr);
_typeCache[typeStr] = type;
}
if (type != null)
{
typeHelper = TypeHelper.Create(type);
typeCode = type.FullName.GetHashCode();
}
_processedTypes[typeCode] = typeHelper;
}
typeHelper.LoadedKey = key;
}
else
{
_processedTypes.TryGetValue(typeCode, out typeHelper);
}
return typeHelper;
}
#endregion
#region Write methods
private void WriteCustomObject(object obj, Type objType, SerializationContext context)
{
context.Write(Codes.CustomObject);
WriteType(objType, context);
long pos = context.Stream.Position;
context.Write(0);
TypeHelper helper = TypeHelper.Create(objType);
foreach (FieldInfo field in
objType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
{
context.Write(field.Name.GetHashCode());
Serialize(helper.GetField(obj, field.Name), context);
}
long end = context.Stream.Position;
context.Stream.Position = pos;
context.Write((int)(end - pos));
context.Stream.Position = end;
}
private void WriteArray(object obj, Type objType, SerializationContext context)
{
context.Write(Codes.Array);
WriteType(objType.GetElementType(), context);
long msPos = context.Stream.Position;
context.Write(0);
Array arr = (Array)obj;
context.Write(arr.Length);
for (int i = 0; i < arr.Length; i++)
{
Serialize(arr.GetValue(i), context);
}
long msEnd = context.Stream.Position;
context.Stream.Position = msPos;
context.Write((int)(msEnd - msPos));
context.Stream.Position = msEnd;
}
private void WriteDictionary(object obj, Type objType, SerializationContext context)
{
context.Write(Codes.Dictionaty);
WriteType(objType, context);
long msPos = context.Stream.Position;
context.Write(0);
context.Write(((IDictionary)obj).Count);
foreach (DictionaryEntry entry in (IDictionary)obj)
{
Serialize(entry.Key, context);
Serialize(entry.Value, context);
}
long msEnd = context.Stream.Position;
context.Stream.Position = msPos;
context.Write((int)(msEnd - msPos));
context.Stream.Position = msEnd;
}
private void WriteList(object obj, Type objType, SerializationContext context)
{
context.Write(Codes.List);
WriteType(objType, context);
long msPos = context.Stream.Position;
context.Write(0);
context.Write(((IList)obj).Count);
IList list = (IList)obj;
for (int i = list.Count - 1; i >= 0; i--)
{
Serialize(list[i], context);
}
long msEnd = context.Stream.Position;
context.Stream.Position = msPos;
context.Write((int)(msEnd - msPos));
context.Stream.Position = msEnd;
}
private void WriteType(Type type, SerializationContext context)
{
int typeKey;
if (!context.ProcessedTypes.TryGetValue(type, out typeKey))
{
typeKey = type.FullName.GetHashCode();
context.ProcessedTypes[type] = typeKey;
context.Write(0);
Tuple<string, long> info;
if (!_typeInfos.TryGetValue(type, out info))
{
string typeName = type.FullName + ", " + type.Assembly.GetName().Name;
DateTime time = File.GetCreationTime(type.Assembly.Location);
info = new Tuple<string, long>(typeName, time.Ticks);
_typeInfos[type] = info;
}
context.Write(info.ItemA);
context.Write(info.ItemB);
}
else
{
context.Write(typeKey);
}
}
#endregion
#region Codes
public static class Codes
{
public const byte Null = 0;
public const byte Int32 = 1;
public const byte Int16 = 2;
public const byte Int64 = 3;
public const byte String = 4;
public const byte CustomObject = 5;
public const byte Array = 6;
public const byte Dictionaty = 7;
public const byte List = 8;
public const byte UInt32 = 9;
public const byte UInt16 = 10;
public const byte UInt64 = 11;
public const byte Byte = 12;
public const byte Boolean = 13;
public const byte Double = 14;
public const byte Single = 15;
public const byte Decimal = 16;
public const byte Reference = 17;
}
#endregion
}
}
DeserializationContext.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Chardex
{
class DeserializationContext
{
private Stream _stream;
private List<object> _addedObjects = new List<object>();
public DeserializationContext(Stream stream)
{
_stream = stream;
}
public List<object> AddedObjects
{
get { return _addedObjects; }
}
public Stream Stream
{
get { return _stream; }
}
public byte ReadByte()
{
byte[] b = new byte[sizeof(byte)];
_stream.Read(b, 0, sizeof(byte));
return b[0];
}
public Boolean ReadBoolean()
{
byte[] b = new byte[sizeof(Boolean)];
_stream.Read(b, 0, sizeof(Boolean));
return BitConverter.ToBoolean(b, 0);
}
public UInt32 ReadUInt32()
{
byte[] b = new byte[sizeof(UInt32)];
_stream.Read(b, 0, sizeof(UInt32));
return BitConverter.ToUInt32(b, 0);
}
public UInt16 ReadUInt16()
{
byte[] b = new byte[sizeof(UInt16)];
_stream.Read(b, 0, sizeof(UInt16));
return BitConverter.ToUInt16(b, 0);
}
public UInt64 ReadUInt64()
{
byte[] b = new byte[sizeof(UInt64)];
_stream.Read(b, 0, sizeof(UInt64));
return BitConverter.ToUInt64(b, 0);
}
public Int32 ReadInt32()
{
byte[] b = new byte[sizeof(Int32)];
_stream.Read(b, 0, sizeof(Int32));
return BitConverter.ToInt32(b, 0);
}
public Int16 ReadInt16()
{
byte[] b = new byte[sizeof(Int16)];
_stream.Read(b, 0, sizeof(Int16));
return BitConverter.ToInt16(b, 0);
}
public Int64 ReadInt64()
{
byte[] b = new byte[sizeof(Int64)];
_stream.Read(b, 0, sizeof(Int64));
return BitConverter.ToInt64(b, 0);
}
public Double ReadDouble()
{
byte[] b = new byte[sizeof(Double)];
_stream.Read(b, 0, sizeof(Double));
return BitConverter.ToDouble(b, 0);
}
public Single ReadSingle()
{
byte[] b = new byte[sizeof(Single)];
_stream.Read(b, 0, sizeof(Single));
return BitConverter.ToSingle(b, 0);
}
public Decimal ReadDecimal()
{
int l = ReadInt32();
int[] bits = new int[l];
for (int i = 0; i < l; i++)
{
bits[i] = ReadInt32();
}
return new decimal(bits);
}
public string ReadString()
{
int l = ReadInt32();
if (l == -1)
return null;
byte[] b = new byte[l];
_stream.Read(b, 0, b.Length);
return Encoding.Default.GetString(b);
}
}
}
SerializationContext.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Chardex
{
class SerializationContext
{
private Stream _stream;
private List<object> _addedObjects = new List<object>();
private Dictionary<Type, int> _processedTypes = new Dictionary<Type, int>();
public SerializationContext(Stream stream)
{
_stream = stream;
}
public Stream Stream
{
get { return _stream; }
}
public List<object> AddedObjects
{
get { return _addedObjects; }
}
public Dictionary<Type, int> ProcessedTypes
{
get { return _processedTypes; }
}
public void Write(byte b)
{
_stream.WriteByte(b);
}
public void Write(Int32 i)
{
_stream.Write(BitConverter.GetBytes(i), 0, sizeof(Int32));
}
public void Write(Int16 i)
{
_stream.Write(BitConverter.GetBytes(i), 0, sizeof(Int16));
}
public void Write(Int64 i)
{
_stream.Write(BitConverter.GetBytes(i), 0, sizeof(Int64));
}
public void Write(Boolean b)
{
_stream.Write(BitConverter.GetBytes(b), 0, sizeof(Boolean));
}
public void Write(UInt32 i)
{
_stream.Write(BitConverter.GetBytes(i), 0, sizeof(UInt32));
}
public void Write(UInt16 i)
{
_stream.Write(BitConverter.GetBytes(i), 0, sizeof(UInt16));
}
public void Write(UInt64 i)
{
_stream.Write(BitConverter.GetBytes(i), 0, sizeof(UInt64));
}
public void Write(Double d)
{
_stream.Write(BitConverter.GetBytes(d), 0, sizeof(Double));
}
public void Write(Single s)
{
_stream.Write(BitConverter.GetBytes(s), 0, sizeof(Single));
}
public void Write(Decimal d)
{
int[] bits = Decimal.GetBits(d);
Write(bits.Length);
for (int i = 0; i < bits.Length; i++)
Write(bits[i]);
}
public void Write(string s)
{
if (s == null)
{
_stream.Write(BitConverter.GetBytes(-1), 0, sizeof(Int32));
return;
}
byte[] bytes = Encoding.Default.GetBytes(s);
_stream.Write(BitConverter.GetBytes(bytes.Length), 0, sizeof(Int32));
_stream.Write(bytes, 0, bytes.Length);
}
}
}
TypeHelper.cs
[c#]
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Chardex
{
public delegate object ConstructorDelegate();
public delegate void ReferenceTypeSetterDelegate(object obj, object val);
public delegate object ValueTypeSetterDelegate(object obj, object val);
public delegate object GetterDelegate(object obj);
public class TypeHelper
{
static Dictionary<Type, TypeHelper> _helpers = new Dictionary<Type, TypeHelper>();
private Type _type;
private ConstructorDelegate _constructor;
private Dictionary<string, GetterDelegate> _getters = new Dictionary<string, GetterDelegate>();
private List<Tuple<int, ReferenceTypeSetterDelegate>> _referenceTypeSetters = new List<Tuple<int, ReferenceTypeSetterDelegate>>();
private List<Tuple<int, ValueTypeSetterDelegate>> _valueTypeSetters = new List<Tuple<int, ValueTypeSetterDelegate>>();
private long _key;
private long _loadedKey;
private bool _isValueType;
private TypeHelper()
{
}
public Type Type
{
get { return _type; }
}
public bool HashChanged
{
get { return _loadedKey != _key; }
}
public static TypeHelper Create(Type type)
{
TypeHelper helper;
if (_helpers.TryGetValue(type, out helper))
return helper;
helper = new TypeHelper();
helper._type = type;
DynamicMethod dmethod = new DynamicMethod("", typeof(object), new Type[] { }, type);
ILGenerator igen = dmethod.GetILGenerator();
if (!type.IsValueType)
{
ConstructorInfo ci = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new CallingConventions(), Type.EmptyTypes, null);
igen.Emit(OpCodes.Newobj, ci);
igen.Emit(OpCodes.Castclass, type);
igen.Emit(OpCodes.Ret);
}
else
{
LocalBuilder lb = igen.DeclareLocal(type);
igen.Emit(OpCodes.Ldloca_S, lb);
igen.Emit(OpCodes.Initobj, type);
igen.Emit(OpCodes.Ldloc_0);
igen.Emit(OpCodes.Box, type);
igen.Emit(OpCodes.Ret);
}
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
{
if (type.IsValueType)
helper.CreateValueTypeSetter(field);
else
helper.CreateReferenceSetter(field);
}
helper._constructor = (ConstructorDelegate)dmethod.CreateDelegate(typeof(ConstructorDelegate));
helper._isValueType = type.IsValueType;
_helpers[type] = helper;
helper._key = System.IO.File.GetCreationTime(type.Assembly.Location).Ticks;
return helper;
}
public void CreateReferenceSetter(FieldInfo fi)
{
DynamicMethod setterMethod = new DynamicMethod("", typeof(void), new Type[] { typeof(object), typeof(object) }, _type);
ILGenerator setterGen = setterMethod.GetILGenerator();
setterGen.Emit(OpCodes.Ldarg_0);
setterGen.Emit(OpCodes.Castclass, _type);
setterGen.Emit(OpCodes.Ldarg_1);
setterGen.Emit(OpCodes.Unbox_Any, fi.FieldType);
setterGen.Emit(OpCodes.Stfld, fi);
setterGen.Emit(OpCodes.Ret);
ReferenceTypeSetterDelegate referenceTypeSetter = (ReferenceTypeSetterDelegate)setterMethod.CreateDelegate(typeof(ReferenceTypeSetterDelegate));
_referenceTypeSetters.Add(new Tuple<int, ReferenceTypeSetterDelegate>(fi.Name.GetHashCode(), referenceTypeSetter));
}
public void CreateValueTypeSetter(FieldInfo fi)
{
DynamicMethod setterMethod = new DynamicMethod("", typeof(object), new Type[] { typeof(object), typeof(object) }, _type);
ILGenerator setterGen = setterMethod.GetILGenerator();
LocalBuilder lb = setterGen.DeclareLocal(_type);
setterGen.Emit(OpCodes.Ldarg_0);
setterGen.Emit(OpCodes.Unbox_Any, _type);
setterGen.Emit(OpCodes.Stloc_0);
setterGen.Emit(OpCodes.Ldloca_S, lb);
setterGen.Emit(OpCodes.Ldarg_1);
setterGen.Emit(OpCodes.Unbox_Any, fi.FieldType);
setterGen.Emit(OpCodes.Stfld, fi);
setterGen.Emit(OpCodes.Ldloc_0);
setterGen.Emit(OpCodes.Box, _type);
setterGen.Emit(OpCodes.Ret);
ValueTypeSetterDelegate valueSetter = (ValueTypeSetterDelegate)setterMethod.CreateDelegate(typeof(ValueTypeSetterDelegate));
_valueTypeSetters.Add(new Tuple<int, ValueTypeSetterDelegate>(fi.Name.GetHashCode(), valueSetter));
}
public object GetField(object obj, string name)
{
GetterDelegate getter;
if (!_getters.TryGetValue(name, out getter))
getter = CreateGetter(name);
return getter(obj);
}
private GetterDelegate CreateGetter(string name)
{
GetterDelegate getter;
FieldInfo fi = _type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (fi == null)
throw new ArgumentException("Field " + name + " not founded", "field");
DynamicMethod getterMethod = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, _type);
ILGenerator getterGen = getterMethod.GetILGenerator();
if (!IsValueType)
{
getterGen.Emit(OpCodes.Ldarg_0);
getterGen.Emit(OpCodes.Castclass, _type);
getterGen.Emit(OpCodes.Ldfld, fi);
if (fi.FieldType.IsValueType)
getterGen.Emit(OpCodes.Box, fi.FieldType);
getterGen.Emit(OpCodes.Ret);
}
else
{
LocalBuilder lb = getterGen.DeclareLocal(_type);
getterGen.Emit(OpCodes.Ldarg_0);
getterGen.Emit(OpCodes.Unbox_Any, _type);
getterGen.Emit(OpCodes.Stloc_0);
getterGen.Emit(OpCodes.Ldloca_S, lb);
getterGen.Emit(OpCodes.Ldfld, fi);
getterGen.Emit(OpCodes.Box, fi.FieldType);
getterGen.Emit(OpCodes.Ret);
}
getter = (GetterDelegate)getterMethod.CreateDelegate(typeof(GetterDelegate));
_getters[name] = getter;
return getter;
}
public object CreateInstance()
{
return _constructor();
}
public List<Tuple<int, ReferenceTypeSetterDelegate>> ReferenceTypeSetters
{
get { return _referenceTypeSetters; }
}
public long Key
{
get { return _key; }
}
public long LoadedKey
{
set { _loadedKey = value; }
}
public bool IsValueType
{
get { return _isValueType; }
}
public List<Tuple<int, ValueTypeSetterDelegate>> ValueTypeSetters
{
get { return _valueTypeSetters; }
}
}
}
[/c#]
Re[4]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Вот проект, вместе с тестом и исходниками. Поддерживает цыклы, в 7-8 раз выигрывает у BinaryFormatter'а по скорости.
Если найдете ошибки, или то, что десериализуется некорректно, сообщите пожалуйста
Re[5]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Chardex, Вы писали:
C>Что-то никому не интересно C>А у меня уже выигрыш в 7,5 раз по сравнению со стандартным...
Вообще, конечно, интересно, но мне, например, хотелось бы увидеть полноценную реализацию IFormatter с поддержкой как минимум интерфейсов ISerializable и IDeserializationCallback на сериализуемых объектах, т.е. полную замену стандартному механизму сериализации. Да и про атрибут NonSerializedAttribute на полях забывать нельзя.
Иначе использовать такую библиотеку именно как замену стандартного BinaryFormatter в реальном приложении будет тяжело.
Re[6]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Oyster, Вы писали:
O>Вообще, конечно, интересно, но мне, например, хотелось бы увидеть полноценную реализацию IFormatter с поддержкой как минимум интерфейсов ISerializable и IDeserializationCallback на сериализуемых объектах, т.е. полную замену стандартному механизму сериализации. Да и про атрибут NonSerializedAttribute на полях забывать нельзя.
Или я не понял роли класса System.Runtime.Serialization.SerializationInfo, или для работы моего форматтера мне надо его переопределить! А он sealed
Подскажите пожалуйста с чего начать. Что-то примеров найти не могу...
Re[7]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Chardex, Вы писали:
C>Все, ссори, разобрался
Да — из него потом можно всё извлечь
Кстати, ещё нельзя забывать про такую полезную штуку, как version check, чтобы сериализация выдала вразумительный exception, если версия объекта поменялась (поля добавились/удалились/изменились), а не "проглотила" это изменение и неправильно заполнила поля.
У меня была идейка на эту тему — взять имена всех полей вместе с типами и сгенерить по этому делу CRC32, который для типа помещать в поток (можно один раз). Если CRC поменялся, то version check failed. Конечно, CRC32 не самый надёжный способ, зато весит мало (на то оно и CRC).
Re[9]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Oyster, Вы писали:
O>Кстати, ещё нельзя забывать про такую полезную штуку, как version check, чтобы сериализация выдала вразумительный exception, если версия объекта поменялась (поля добавились/удалились/изменились), а не "проглотила" это изменение и неправильно заполнила поля.
У меня сейчас работает так: при сохранении объекта вместе с ним сохраняется некий ключ, по которому при восстановлении можно проверить был ли тип изменен (На данный момент сделал в лоб — этот ключ — дата создания сборки, где тип... ). Если тип не был измененен, сериализация идет без проверок и лишних затрат. Если тип был изменен — то поля которые были удалены/добавлены/изменен_тип просто не будут затронуты и не будет exception'ов. Хотя можно кидать их без проблем, хоть опционально Только у меня вместо имен полей, для повышения производительности, сохраняются их хеши, так что имя поля в исключении дать не смогу.
Вообщем при изменении версии типа объекта, вся неизменнная часть графа восстанавливается. O>У меня была идейка на эту тему — взять имена всех полей вместе с типами и сгенерить по этому делу CRC32, который для типа помещать в поток (можно один раз). Если CRC поменялся, то version check failed. Конечно, CRC32 не самый надёжный способ, зато весит мало (на то оно и CRC).
Я конечно понимаю, что моя версия с датой сборки — неправильное решение, но кому и зачем надо будет ее менять Чем она плоха?)
Re[10]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Chardex, Вы писали:
C>Я конечно понимаю, что моя версия с датой сборки — неправильное решение, но кому и зачем надо будет ее менять Чем она плоха?)
Решение с датой сборки в общем-то неплохое (я просто не додумался до него), но оно не будет работать с динамическими сборками (с генерацией типов в рантайме через Reflection.Emit), т.к. для динамической сборки дата создания будет новая при каждом запуске приложения. Т.е., например, прокси-типы, создаваемые на лету в BLToolkit
Здравствуйте, Oyster, Вы писали:
O>Решение с датой сборки в общем-то неплохое (я просто не додумался до него), но оно не будет работать с динамическими сборками (с генерацией типов в рантайме через Reflection.Emit), т.к. для динамической сборки дата создания будет новая при каждом запуске приложения. Т.е., например, прокси-типы, создаваемые на лету в BLToolkit
(и в моём приложении, например), десериализовать не удастся при следующем запуске приложения...
Еще как удастся, только будет лишние наклодные расходы на проверку неизменности данных — медленнее будет десериализовываться, но все равно гораздо быстрее стандартного.
Попробую с CRC32 в ближайшее время.
Re[12]: [C#] Сериализатор, более быстрый чем BinaryFormatter
Здравствуйте, Chardex, Вы писали:
C>Еще как удастся, только будет лишние наклодные расходы на проверку неизменности данных — медленнее будет десериализовываться, но все равно гораздо быстрее стандартного.
Добавил поддержку ISerializable, IDeserializationCallback, IDeserializationCallback. Пронаследовался от IFormatter, но не пойму пока что мне делать с SurrogateSelector и Binder'ом. Как я понял, Binder это для случая если тип изменился, конвертации между типами и т.п., Context просто для установления где (для чего) у нас проиходит сериализация, а вот SurrogateSelector вообще И еще я исполизую стандартный FormatterConverter для передачи в констурктор SerializationInfo, это нормально?
Можно ли мой форматтер прикрутить к ремоутингу?
Здравствуйте, Chardex, Вы писали:
C>Здравствуйте, Chardex, Вы писали:
C>Добавил поддержку ISerializable, IDeserializationCallback, IDeserializationCallback. Пронаследовался от IFormatter, но не пойму пока что мне делать с SurrogateSelector и Binder'ом. Как я понял, Binder это для случая если тип изменился, конвертации между типами и т.п., Context просто для установления где (для чего) у нас проиходит сериализация, а вот SurrogateSelector вообще И еще я исполизую стандартный FormatterConverter для передачи в констурктор SerializationInfo, это нормально? C>Можно ли мой форматтер прикрутить к ремоутингу?
Вещь довольно интересная, хотя есть несколько замечаний:
1. Быстродействие сериалайзера можно увеличить, храня индексы уже сериализованных объектов в Dictionary, а не в List. А то делать постоянно по List'у Contains как-то некузяво.
2. Сериализовывать классы, реализующие IList и IDictionary в самом сериалайзере тоже не есть правильно, т.к. неизвестно заранее, какие именно данные нужно сериализовывать этим классам. А вот сериализовывать более компактно DataSet/DataTable было бы неплохо.
3. С сериализацией generic-типов как-то не очень.
4. В выложенных исходниках я не заметил поддержки ISerializable, NonSerialized, OptionalAttribute, Binder'а и т.п.
5. Ну и вообще там много чего соптимизировать можно как по скорости, так и по размеру хранимых данных и концептуально тоже.
Если есть желание, мог бы с некоторыми моментами помочь, т.к. у меня как раз стоит сейчас задача написания подобной штуки.
P.S. Если все требуемые интерфейсы поддерживаются, то прикрутить эту штуку к ремоутингу вполне реально.
Здравствуйте, VladiCh, Вы писали:
VC>4. В выложенных исходниках я не заметил поддержки ISerializable, NonSerialized, OptionalAttribute, Binder'а и т.п.
Здравствуйте, Chardex, Вы писали:
C>... а вот SurrogateSelector вообще
SurrogateSelector нужен для того, чтобы по типу выбрать ISerializationSurrogate, который позволяет нужный тип сериализовать "вручную", как если бы тот реализовывал ISerializable. Очень полезная штука, на самом деле.