简体   繁体   English

Reflection.Emit 和实例化

[英]Reflection.Emit and Instantiation

I have an assembly and some classes.我有一个程序集和一些课程。 What I'm trying to do is create an instance of a class and fill its properties in a generic way, something like:我要做的是创建一个 class 的实例并以通用方式填充其属性,例如:

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

Reflection is the best way but its too slow, instead I've heard that Reflection.Emit is faster, so, is there a way to instantiate the class and fill its properties with Reflection.Emit?反射是最好的方法,但它太慢了,相反我听说 Reflection.Emit 更快,那么,有没有办法实例化 class 并用 Reflection.Emit 填充其属性?

Thanks in advance for any help.提前感谢您的帮助。

On this occasion, I suggest HyperDescriptor ;在这种情况下,我建议HyperDescriptor it is like reflection, but with IL generation thrown in the middle for performance;类似于反射,但为了性能而将 IL 生成置于中间; then, you just use regular component-model code:然后,您只需使用常规的组件模型代码:

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;编辑; for a bit of a 2012 update, FastMember involves less abstraction:对于 2012 年的一些更新, FastMember涉及的抽象较少:

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

In addition to being more direct, FastMember will work with properly dynamic types, too - not just reflection.除了更直接之外,FastMember 也可以使用正确的dynamic类型——不仅仅是反射。

If you're on.Net 4, you could use the new Expression types (that were added to support dynamic ), to create an expression that assigns the properties, compile it to a delegate once, and call that repeatedly.如果您使用的是.Net 4,则可以使用新的Expression类型(添加以支持dynamic )来创建分配属性的表达式,将其编译为委托一次,然后重复调用。 The performance should be comparable with using Reflection.Emit.性能应该与使用 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);
    }
}

You could do something very similar in.Net 3 too, using the Expression version of object initializers.您也可以使用 object 初始化程序的Expression版本在.Net 3 中执行非常类似的操作。

Dapper.NET does this. Dapper.NET 就是这样做的。 It's a Micro-ORM made to power stackoverflow.这是一个为stackoverflow提供动力的Micro-ORM。 The whole ORM is just one C# file.整个 ORM 只是一个 C# 文件。

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

The relevant code that creates the dynamic method is this:创建动态方法的相关代码是这样的:

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>));

Also note that the IKVM.NET project provides their own implementation of IKVM.Reflection.Emit with a number of enhancements.另请注意, IKVM.NET项目提供了他们自己的IKVM.Reflection.Emit实现,并具有许多增强功能。 If you're starting from scratch it might be good to look at that as an alternative.如果您是从头开始,那么将其视为替代方案可能会很好。

Here is an example article on how to do mapping from a database IDatarecord.这是一篇关于如何从数据库 IDatarecord 进行映射的示例文章。 Its easy to adapt this to most scenarios它很容易适应大多数情况

Dynamic... But Fast: The Tale of Three Monkeys, A Wolf and the DynamicMethod and ILGenerator Classes http://www.codeproject.com/KB/database/DynamicMethod_ILGenerator.aspx动态的......但速度很快:三只猴子、一只狼的故事以及 DynamicMethod 和 ILGenerator 类http://www.codeproject.com/KB/database/DynamicMethod_ILGenerator.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM