3

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.

Argons
  • 445
  • 1
  • 6
  • 23

4 Answers4

6

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

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.

svick
  • 236,525
  • 50
  • 385
  • 514
2

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.

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • Note that we have the advantage that data-grids tend to have the same columns - not necessarily true with dictionary maps; something similar would work, but it would be more complex, as it would need to switch on the keys. – Marc Gravell Jul 01 '11 at 22:26
  • @Marc Gravell, true, but if the point is to create an instance of `T`, then it's probably a good assumption that the dictionary keys will be the same. Besides that, mapping keys was not part of the question. :-) And finally, Dapper.NET is really awesome, so any chance to hype it up is good. I wish more frameworks would use Reflection.Emit instead of plain-old Reflection. – Samuel Neff Jul 01 '11 at 22:33
  • there are intermediate options, though (dapper really is pretty specialized; I'm not sure I'd use it as comparison here...) – Marc Gravell Jul 01 '11 at 22:51
  • @Marc Gravell, that is true. In our application we actually generate builders at compile time. Much easier to do and faster at runtime. If you can define the class at compile time, there's no reason you can't also generate a class-specific builder. – Samuel Neff Jul 02 '11 at 01:37
0

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

rqmedes
  • 540
  • 2
  • 14