简体   繁体   中英

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:

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?

Thanks in advance for any help.

On this occasion, I suggest HyperDescriptor ; it is like reflection, but with IL generation thrown in the middle for performance; 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:

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.

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. The performance should be comparable with using 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.

Dapper.NET does this. It's a Micro-ORM made to power stackoverflow. The whole ORM is just one C# file.

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. 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. 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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