ملكية الكائن الديناميكي Populator (بدون انعكاس)

StackOverflow https://stackoverflow.com/questions/1273589

  •  16-09-2019
  •  | 
  •  

سؤال

أريد أن أملك خصائص كائن دون استخدام التفكير بطريقة مماثلة ل Dynamicbuilder على CodeProject.. وبعد مثال CodeProject مصمم خصيصا للكيانات المرتبية باستخدام DataReader أو DataRecord. أنا استخدم هذا في العديد من الوسائل إلى تأثير جيد. الآن أريد تعديله لاستخدام القاموس أو الكائن غير المرغوي للبيانات الأخرى حتى أتمكن من استخدامه في كود غير DAL - ألافيا التي أستخدمها حاليا الانعكاس. لا أعرف شيئا تقريبا عن Opcodes و IL. أنا أعرف أن ذلك يعمل بشكل جيد وهو أسرع من التفكير.

لقد حاولت تعديل مثال CodeProject، وبسبب جهلي مع IL، فقد حصلت على عالقة على سطرين.

  • واحد منهم يتعامل مع dbnulls وأنا متأكد من أنني يمكن أن أخسرها فقط، لكنني لا أعرف إذا كانت الخطوط التي تسبقها ومتابعة أنها ذات صلة وأي منهم ستحتاج أيضا إلى الذهاب.
  • الآخر، أعتقد أنه هو الذي سحبت القيمة من DataRecord من قبل ويحتاج الآن إلى سحبه من القاموس. أعتقد أنه يمكنني استبدال "GetValuemethod" مع "الخاصية" الخاصة بي "لكنني لست متأكدا.

أنا منفتح على طرق بديلة / أفضل لصنع هذه القط أيضا.

إليك الرمز حتى الآن (الخطوط الخارجية هي تلك التي علقتها):

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

public class Populator<T>
{
    private delegate T Load(Dictionary<string, object> properties);
    private Load _handler;
    private Populator() { }
    public T Build(Dictionary<string, object> properties)
    {
        return _handler(properties);
    }
    public static Populator<T> CreateBuilder(Dictionary<string, object> properties)
    {
        //private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new [] { typeof(int) });
        //private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new [] { typeof(int) });
        Populator<T> dynamicBuilder = new Populator<T>();
        DynamicMethod method = new DynamicMethod("Create", typeof(T), new[] { typeof(Dictionary<string, object>) }, typeof(T), true);
        ILGenerator generator = method.GetILGenerator();
        LocalBuilder result = generator.DeclareLocal(typeof(T));
        generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
        generator.Emit(OpCodes.Stloc, result);
        int i = 0;
        foreach (var property in properties)
        {
            PropertyInfo propertyInfo = typeof(T).GetProperty(property.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy | BindingFlags.Default);
            Label endIfLabel = generator.DefineLabel();

            if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
            {
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, i);
                //generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                generator.Emit(OpCodes.Brtrue, endIfLabel);

                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, i);
                //generator.Emit(OpCodes.Callvirt, getValueMethod);

                generator.Emit(OpCodes.Unbox_Any, property.Value.GetType());
                generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                generator.MarkLabel(endIfLabel);
            }
            i++;
        }

        generator.Emit(OpCodes.Ldloc, result);
        generator.Emit(OpCodes.Ret);
        dynamicBuilder._handler = (Load)method.CreateDelegate(typeof(Load));
        return dynamicBuilder;
    }
}

تعديل:

باستخدام تطبيق PropertyDescriptor Marc Gravell (مع ارتفاع ضغط الدم) يتم تبسيط الرمز مائة طي. لدي الآن الاختبار التالي:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Hyper.ComponentModel;

namespace Test
{
    class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    class Program
    {
        static void Main()
        {
            HyperTypeDescriptionProvider.Add(typeof(Person));
            var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } };
            Person person = new Person();
            DynamicUpdate(person, properties);
            Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name);
            Console.ReadKey();
        }

        public static void DynamicUpdate<T>(T entity, Dictionary<string, object> properties)
        {
            foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
                if (properties.ContainsKey(propertyDescriptor.Name))
                    propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]);
        }
    }
}

أي تعليقات على اعتبارات الأداء لكل من TypedEScriptor.getProperties () و PropertyDescriptor.setValue () هي موضع ترحيب ...

هل كانت مفيدة؟

المحلول

تحرير: كل هذا هو في الأساس ما dapper يفعل - ولكن dapper أكثر الأمثل. إذا كنت أكتب هذه الإجابة اليوم، فسوف يقرأ ببساطة: "استخدام Dapper".


إذا لم تكن مفيدا "على IL، فهناك بدائل تحصل على سرعة IL وراحة الانعكاس.

المثال الأول:

hyperdescriptor. - يستخدم مخصص PropertyDescriptor النموذج الذي يتعامل مع IL لك، لذلك كل ما لديك هو رمز مثل (بالإضافة إلى بطانة واحدة لتمكين HyperDescriptor):

public static IEnumerable<T> Read<T>(IDataReader reader) where T : class, new() 
{
    PropertyDescriptorCollection props =
        TypeDescriptor.GetProperties(typeof(T));

    PropertyDescriptor[] propArray = new PropertyDescriptor[reader.FieldCount];
    for (int i = 0; i < propArray.Length; i++)
    {
        propArray[i] = props[reader.GetName(i)];
    }
    while(reader.Read()) {
        T item = new T();
        for (int i = 0; i < propArray.Length; i++)
        {
            object value = reader.IsDBNull(i) ? null : reader[i];
            propArray[i].SetValue(item, value);
        }
        yield return item;
    }
}

المثال الثاني:

تعبيرات Linq - طويل جدا، لكنني ناقشت هذا (وما سبق، اتضح) على Usenet - انظر هذا الأرشيف.

نصائح أخرى

نعم، يمكنك استخدام رمز مثل هذا:

 for (int i = 0; i < dataRecord.FieldCount; i++)
                {

                    PropertyInfo propertyInfo = t.GetProperty(dataRecord.GetName(i));
                    LocalBuilder il_P = generator.DeclareLocal(typeof(PropertyInfo));

                    Label endIfLabel = generator.DefineLabel();

.... ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top