微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用动态发出的POCO进行快速序列化和反序列化

如何解决使用动态发出的POCO进行快速序列化和反序列化

评论和聊天看来,其中的关键部分仍然是创建动态类型。好的,这是一个完整的示例,显示一个完全可序列化的(通过任何常见的序列化程序)类型。您当然可以在类型中添加更多内容- 可能是索引器,以按数字或按名称INotifyPropertyChanged获取属性

另外-关键点:您 缓存并重新使用生成Type实例。不要 守再生这个东西......你会出血内存。

using Newtonsoft.Json;
using ProtoBuf;
using System;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;

public interface IBasicRecord
{
    object this[int field] { get; set; }
}
class Program
{
    static void Main()
    {
        object o = 1;
        int foo = (int)o;
        string[] names = { "Id", "Name", "Size", "When" };
        Type[] types = { typeof(int), typeof(string), typeof(float), typeof(DateTime?) };

        var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(
            new AssemblyName("DynamicStuff"),
            AssemblyBuilderAccess.Run);
        var module = asm.DefineDynamicModule("DynamicStuff");
        var tb = module.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Serializable);
        tb.SetCustomAttribute(new CustomAttributeBuilder(
            typeof(DataContractAttribute).GetConstructor(Type.EmptyTypes), new object[0]));
        tb.AddInterfaceImplementation(typeof(IBasicRecord));

        FieldBuilder[] fields = new FieldBuilder[names.Length];
        var dataMemberCtor = typeof(DataMemberAttribute).GetConstructor(Type.EmptyTypes);
        var dataMemberProps = new[] { typeof(DataMemberAttribute).GetProperty("Order") };
        for (int i = 0; i < fields.Length; i++)
        {
            var field = fields[i] = tb.DefineField("_" + names[i],
                types[i], FieldAttributes.Private);

            var prop = tb.DefineProperty(names[i], PropertyAttributes.None,
                types[i], Type.EmptyTypes);
            var getter = tb.DefineMethod("get_" + names[i],
                MethodAttributes.Public | MethodAttributes.HideBySig, types[i], Type.EmptyTypes);
            prop.Setgetmethod(getter);
            var il = getter.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0); // this
            il.Emit(OpCodes.Ldfld, field); // .Foo
            il.Emit(OpCodes.Ret); // return
            var setter = tb.DefineMethod("set_" + names[i],
                MethodAttributes.Public | MethodAttributes.HideBySig, typeof(void), new Type[] { types[i] });
            prop.SetSetMethod(setter);
            il = setter.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0); // this
            il.Emit(OpCodes.Ldarg_1); // value
            il.Emit(OpCodes.Stfld, field); // .Foo =
            il.Emit(OpCodes.Ret);

            prop.SetCustomAttribute(new CustomAttributeBuilder(
                dataMemberCtor, new object[0],
                dataMemberProps, new object[1] { i + 1 }));
        }

        foreach (var prop in typeof(IBasicRecord).GetProperties())
        {
            var accessor = prop.Getgetmethod();
            if (accessor != null)
            {
                var args = accessor.GetParameters();
                var argTypes = Array.ConvertAll(args, a => a.ParameterType);
                var method = tb.DefineMethod(accessor.Name,
                    accessor.Attributes & ~MethodAttributes.Abstract,
                    accessor.CallingConvention, accessor.ReturnType, argTypes);
                tb.DefineMethodoverride(method, accessor);
                var il = method.GetILGenerator();
                if (args.Length == 1 && argTypes[0] == typeof(int))
                {
                    var branches = new Label[fields.Length];
                    for (int i = 0; i < fields.Length; i++)
                    {
                        branches[i] = il.DefineLabel();
                    }
                    il.Emit(OpCodes.Ldarg_1); // key
                    il.Emit(OpCodes.Switch, branches); // switch
                    // default:
                    il.ThrowException(typeof(ArgumentOutOfRangeException));
                    for (int i = 0; i < fields.Length; i++)
                    {
                        il.MarkLabel(branches[i]);
                        il.Emit(OpCodes.Ldarg_0); // this
                        il.Emit(OpCodes.Ldfld, fields[i]); // .Foo
                        if (types[i].IsValueType)
                        {
                            il.Emit(OpCodes.Box, types[i]); // (object)
                        }
                        il.Emit(OpCodes.Ret); // return
                    }
                }
                else
                {
                    il.ThrowException(typeof(NotImplementedException));
                }
            }
            accessor = prop.GetSetMethod();
            if (accessor != null)
            {
                var args = accessor.GetParameters();
                var argTypes = Array.ConvertAll(args, a => a.ParameterType);
                var method = tb.DefineMethod(accessor.Name,
                    accessor.Attributes & ~MethodAttributes.Abstract,
                    accessor.CallingConvention, accessor.ReturnType, argTypes);
                tb.DefineMethodoverride(method, accessor);
                var il = method.GetILGenerator();
                if (args.Length == 2 && argTypes[0] == typeof(int) && argTypes[1] == typeof(object))
                {
                    var branches = new Label[fields.Length];
                    for (int i = 0; i < fields.Length; i++)
                    {
                        branches[i] = il.DefineLabel();
                    }
                    il.Emit(OpCodes.Ldarg_1); // key
                    il.Emit(OpCodes.Switch, branches); // switch
                    // default:
                    il.ThrowException(typeof(ArgumentOutOfRangeException));
                    for (int i = 0; i < fields.Length; i++)
                    {
                        il.MarkLabel(branches[i]);
                        il.Emit(OpCodes.Ldarg_0); // this
                        il.Emit(OpCodes.Ldarg_2); // value
                        il.Emit(types[i].IsValueType ? OpCodes.UnBox_Any : OpCodes.Castclass, types[i]); // (SomeType)
                        il.Emit(OpCodes.Stfld, fields[i]); // .Foo =
                        il.Emit(OpCodes.Ret); // return
                    }
                }
                else
                {
                    il.ThrowException(typeof(NotImplementedException));
                }
            }
        }

        var type = tb.CreateType();
        var obj = Activator.CreateInstance(type);
        // we'll use the index (via a kNown interface) to set the values
        IBasicRecord rec = (IBasicRecord)obj;
        rec[0] = 123;
        rec[1] = "abc";
        rec[2] = 12F;
        rec[3] = DateTime.Now;
        for (int i = 0; i < 4; i++)
        {
            Console.WriteLine("{0} = {1}", i, rec[i]);
        }
        using (var ms = new MemoryStream())
        {
            var ser = new XmlSerializer(type);
            ser.Serialize(ms, obj);
            Console.WriteLine("XmlSerializer: {0} bytes", ms.Length);
        }
        using (var ms = new MemoryStream())
        {
            using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true))
            {
                var ser = new JsonSerializer();
                ser.Serialize(writer, obj);
            }
            Console.WriteLine("Json.NET: {0} bytes", ms.Length);
        }
        using (var ms = new MemoryStream())
        {
            var ser = new DataContractSerializer(type);
            ser.WriteObject(ms, obj);
            Console.WriteLine("DataContractSerializer: {0} bytes", ms.Length);
        }
        using (var ms = new MemoryStream())
        {
            Serializer.NonGeneric.Serialize(ms, obj);
            Console.WriteLine("protobuf-net: {0} bytes", ms.Length);
        }
        using (var ms = new MemoryStream())
        {
            // note: NEVER do this unless you have a custom Binder; your
            // assembly WILL NOT deserialize in the next AppDomain (i.e.
            // the next time you load your app, you won't be able to load)
            // - shown only for illustration
            var bf = new BinaryFormatter();
            bf.Serialize(ms, obj);
            Console.WriteLine("BinaryFormatter: {0} bytes", ms.Length);
        }
    }
}

输出

XmlSerializer: 246 bytes
Json.NET: 81 bytes
DataContractSerializer: 207 bytes
protobuf-net: 25 bytes
BinaryFormatter: 182 bytes

解决方法

我目前正在将SQL表行序列化为二进制格式,以进行有效存储。我将二进制数据序列化/反序列化为List<object>每行。我正在尝试将其升级为使用POCO,该POCO将动态生成(发射),每列一个字段。

我一直在网上搜索数小时,偶然发现了EF,T4,ExpandoObject之类的ORM
/框架,但所有这些要么使用动态对象(可以动态添加/删除属性),要么在编译之前简单地生成POCO。我不能使用模板,因为在编译时表的模式是未知的,并且使用动态对象会太过费力(并且很慢),因为我知道确切的属性集和类型。我需要为每个表生成一个POCO,其中Fields对应于列,并相应地设置数据类型(INT->
int,TEXT->字符串)。

生成POCO之后,我将继续使用发出的CIL获取/设置属性,就像PetaPoco对静态编译的POCO所做的一样。我希望所有这些技巧都比使用无类型列表更快,并为我提供高强度POCO,这些POCO具有强类型并且可以通过CLR加速。我是否正确地假设了这一点?您可以在运行时开始生成POCO吗?并且使用POCO会比使用POCO更快或更有效List<object>吗?基本上,值得为此烦恼吗?我已经知道如何使用发出的CIL加速获取/设置字段。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。