Argons
Argons

Reputation: 445

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.

Upvotes: 3

Views: 1172

Answers (4)

Marc Gravell
Marc Gravell

Reputation: 1064204

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.

Upvotes: 6

user749687
user749687

Reputation:

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

Upvotes: 0

svick
svick

Reputation: 245046

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.

Upvotes: 3

Samuel Neff
Samuel Neff

Reputation: 74949

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.

Upvotes: 2

Related Questions