Reflection.Emit и Instantiation - PullRequest
       26

Reflection.Emit и Instantiation

3 голосов
/ 02 июля 2011

У меня есть сборка и несколько классов. Я пытаюсь создать экземпляр класса и заполнить его свойства универсальным способом, например:

public T FillObject(IDictionary<string,object> values)
{
    /// CREATE INSTANCE
    /// FILL THE PROPERTIES WITH THE VALUES
}

Reflection - лучший способ, но он слишком медленный, вместо этого я слышал, что Reflection.Emit быстрее, поэтому есть ли способ создать экземпляр класса и заполнить его свойства Reflection.Emit?

Заранее спасибо за любую помощь.

Ответы [ 4 ]

6 голосов
/ 02 июля 2011

В этом случае я предлагаю HyperDescriptor ; это как отражение, но с производительностью IL, брошенной в середину для производительности; тогда вы просто используете обычный код модели компонента:

object obj = Activator.CreateInstance(typeof(T));
var props = TypeDescriptor.GetProperties(typeof(T));
foreach(var pair in data)
{
    props[pair.Key].SetValue(obj, pair.Value);
}

Edit; для небольшого обновления 2012 года FastMember включает меньше абстракции:

var accessor = TypeAccessor.Create(typeof(T));
foreach(var pair in data)
{
    accessor[obj, pair.Key] = pair.Value;
}

В дополнение к тому, что FastMember более прямолинеен, он будет работать и с правильными dynamic типами, а не только с отражением.

3 голосов
/ 02 июля 2011

Если вы используете .Net 4, вы можете использовать новые типы Expression (которые были добавлены для поддержки dynamic), чтобы создать выражение, которое присваивает свойства, один раз скомпилировать его для делегата и вызвать это неоднократно. Производительность должна быть сопоставима с использованием Reflection.Emit.

class ObjectFiller<T>
{
    private static Func<IDictionary<string, object>, T> FillerDelegate;

    private static void Init()
    {
        var obj = Expression.Parameter(typeof(T), "obj");
        var valuesDictionary = Expression.Parameter(typeof(IDictionary<string, object>), "values");

        var create = Expression.Assign(
            obj, Expression.Call(typeof(Activator), "CreateInstance", new[] { typeof(T) }));

        var properties = typeof(T).GetProperties();

        var setters = Expression.Block(properties.Select(p => CreateSetter(p, obj, valuesDictionary)));

        var methodBody = Expression.Block(typeof(T), new[] { obj }, create, setters, obj);

        var fillerExpression = Expression.Lambda<Func<IDictionary<string, object>, T>>(methodBody, valuesDictionary);

        FillerDelegate = fillerExpression.Compile();
    }

    static Expression CreateSetter(PropertyInfo property, Expression obj, Expression valuesDictionary)
    {
        var indexer = Expression.MakeIndex(
            valuesDictionary,
            typeof(IDictionary<string, object>).GetProperty("Item", new[] { typeof(string) }),
            new[] { Expression.Constant(property.Name) });

        var setter = Expression.Assign(
            Expression.Property(obj, property),
            Expression.Convert(indexer, property.PropertyType));

        var valuesContainsProperty = Expression.Call(
            valuesDictionary, "ContainsKey", null, Expression.Constant(property.Name));

        var condition = Expression.IfThen(valuesContainsProperty, setter);

        return condition;
    }

    public T FillObject(IDictionary<string, object> values)
    {
        if (FillerDelegate == null)
            Init();
        return FillerDelegate(values);
    }
}

Вы могли бы сделать нечто очень похожее и в .Net 3, используя Expression версию инициализаторов объектов.

2 голосов
/ 02 июля 2011

Dapper.NET делает это.Это микро-ORM, созданный для питания переполнения стека.Весь ORM - это всего лишь один файл C #.

http://code.google.com/p/dapper-dot-net/source/browse/Dapper/SqlMapper.cs

Соответствующий код, который создает динамический метод, таков:

var method = new DynamicMethod(commandType.Name + "_BindByName", null, new Type[] { typeof(IDbCommand), typeof(bool) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, commandType);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, setter, null);
il.Emit(OpCodes.Ret);
action = (Action<IDbCommand, bool>)method.CreateDelegate(typeof(Action<IDbCommand, bool>));

Также обратите внимание, что * 1011Проект * IKVM.NET предоставляет собственную реализацию IKVM.Reflection.Emit с рядом улучшений.Если вы начинаете с нуля, было бы неплохо рассмотреть это как альтернативу.

0 голосов
/ 06 июля 2011

Вот пример статьи о том, как сделать отображение из базы данных IDatarecord. Его легко адаптировать к большинству сценариев

Динамично ... Но Быстро: Сказка о Трех Обезьянах, Волке и Классах DynamicMethod и ILGenerator http://www.codeproject.com/KB/database/DynamicMethod_ILGenerator.aspx

...