当前位置: 首页 > 面试题库 >

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

左丘修齐
2023-03-14
问题内容

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

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

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


 类似资料:
  • 本文向大家介绍C#使用Json.Net进行序列化和反序列化及定制化,包括了C#使用Json.Net进行序列化和反序列化及定制化的使用技巧和注意事项,需要的朋友参考一下 序列化(Serialize)是将对象转换成字节流,并将其用于存储或传输的过程,主要用途是保存对象的状态,以便在需要时重新创建该对象;反序列化(Deserialize)则是将上面的字节流转换为相应对象的过程;在.Net阵营中,Json

  • 本文向大家介绍Python使用pickle进行序列化和反序列化的示例代码,包括了Python使用pickle进行序列化和反序列化的示例代码的使用技巧和注意事项,需要的朋友参考一下 一、说明 早上看到Python使用pickle进行序列化和反序列化,然后发现面临的一个获取不到返回值的框架,似乎可以通过在框架中先序列化,然后在外部进行反序列化的方法来实现。就研究了一下pickle库的具体使用。 本身也

  • I'va是一个OID接口,可以由许多具体类型实现: 现在我有一个具有两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义 我想使用jackson以不同的方式序列化/反序列化字段,无论它们是使用抽象接口类型还是具体类型定义的: 注意,被序列化,包括类型信息(多态序列化),而被序列化为文本 为此,我将OID接口注释为: 并为每个具体类型分配了类型id: 最后,对容器

  • 问题内容: 我是C ++的新手。使用序列化和反序列化类型数据的最简单方法是什么。我发现了一些使用示例,但它们对我来说是晦涩的。 问题答案: 请注意,将键解释为路径,例如,将对“ ab” =“ z”放置将创建{“ a”:{“ b”:“ z”}} JSON,而不是{“ ab”:“ z”} 。否则,使用是微不足道的。这是一个小例子。

  • 我有一个字段,它通过类序列化为ISO-8601格式。 当我收到以下JSON时, 通过将jackson-datatype-jsr310模块包含到项目中,并使用和解决了这个问题。

  • 问题内容: 我正在从如下所示的Web API接收JSON数据: 我将此数据反序列化为以下类型的对象: 稍后在我的应用程序中,我想再次将ErrorDetails对象序列化为JSON,但使用属性名称代替。因此结果将如下所示: 有什么简单的方法可以使用Json.Net完成此操作?也许使用自定义解析器和一些属性,例如: 但是解析器不会告诉我何时进行序列化或反序列化。 问题答案: 您可以使用,Contrac