加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 使用动态发射的POCO进行快速序列化和反序列化

发布时间:2020-12-16 00:23:00 所属栏目:百科 来源:网络整理
导读:我目前正在将SQL表行序列化为二进制格式以实现高效存储.我将二进制数据序列化/反序列化为List object每排.我正在尝试将其升级为使用POCO,这将通过每列一个字段动态生成(发出). 我一直在网上搜索几个小时,偶然发现了像EF,T4,ExpandoObject这样的ORM /框架,但
我目前正在将SQL表行序列化为二进制格式以实现高效存储.我将二进制数据序列化/反序列化为List< object>每排.我正在尝试将其升级为使用POCO,这将通过每列一个字段动态生成(发出).

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

在生成POCO之后,我将继续使用发出的CIL来获取/设置属性,就像PetaPoco does for statically compiled POCOs一样.我希望所有这些钻井平台都比使用无类型列表更快,并且给我高强度的POCO – 键入并可以由CLR加速.我认为这是正确的吗?你可以在运行时创建POCO吗?与使用List< object>相比,使用POCO会更快或更节省内存吗?基本上,值得麻烦吗?我已经知道如何使用发出的CIL加速获取/设置字段.

解决方法

从评论和聊天中,似乎关键部分仍然是创建动态类型;好的,这是一个完整的示例,显示完全可序列化(通过任何常见的序列化程序)类型.您当然可以添加更多类型 – 可能是索引器按数字或名称获取属性,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,Type.EmptyTypes);
            var getter = tb.DefineMethod("get_" + names[i],MethodAttributes.Public | MethodAttributes.HideBySig,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],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,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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读