Runtime compiler for getting/setting runtime property values I've been playing about with runtime compilation to allow me to get/set runtime property values in C#, and so far I have come up with a class.

This allows me to write code like the following which works well and is much faster than standard reflection:


TypeWrapper wrapper = new TypeWrapper(target);
wrapper.Set("Value", "abc");
return (string)wrapper.Get("Value");



What I'm wondering is whether there is a way to make this even faster. I could go into metaprogramming and use Reflection.Emit,  but that adds a vastly increased order of complexity for other developers to follow.

Is there something obvious you can see?

Here's the current benchmarks using Benchmark.NET:




Total time: 00:01:47 (107.64 sec)

// * Summary *

BenchmarkDotNet=v0.9.4.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz, ProcessorCount=12
Frequency=3117484 ticks, Resolution=320.7715 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
JitModules=clrjit-v4.6.1063.1


                     Method |      Median |     StdDev | Scaled |
--------------------------- |------------ |----------- |------- |
               1. Static C# |   2.5401 ns |  0.6457 ns |   1.00 |
              2. Dynamic C# |  35.1664 ns |  0.4643 ns |  13.84 |
            3. PropertyInfo | 430.8785 ns |  6.4399 ns | 169.63 |
      4. PropertyDescriptor | 920.3483 ns | 59.1531 ns | 362.32 |
             5. TypeWrapper | 117.0505 ns |  1.6790 ns |  46.08 |

// ***** BenchmarkRunner: End *****

1. Static C#: 2.54 ns
2. Dynamic C#: 35.17 ns
3. PropertyInfo: 430.88 ns
4. PropertyDescriptor: 920.35 ns
5. TypeWrapper: 117.05 ns



And the class itself:



public class TypeWrapper
{
    private readonly dynamic dyn;
    private readonly Dictionary&lt;string, CallSite&lt;Action&lt;CallSite, object, object&gt;&gt;&gt; setters
        = new Dictionary&lt;string, CallSite&lt;Action&lt;CallSite, object, object&gt;&gt;&gt;();

    private readonly Dictionary&lt;string, CallSite&lt;Func&lt;CallSite, object, object&gt;&gt;&gt; getters
        = new Dictionary&lt;string, CallSite&lt;Func&lt;CallSite, object, object&gt;&gt;&gt;();

    public TypeWrapper(object d)
    {
        this.dyn = d;
        Type type = d.GetType();
        foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            string name = p.Name;
            CallSite&lt;Action&lt;CallSite, object, object&gt;&gt; set = CallSite&lt;Action&lt;CallSite, object, object&gt;&gt;.Create(
            Microsoft.CSharp.RuntimeBinder.Binder.SetMember(
                CSharpBinderFlags.None,
                name,
                type,
                new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) ,
                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));

            this.setters.Add(name, set);

            CallSite&lt;Func&lt;CallSite, object, object&gt;&gt; get = CallSite&lt;Func&lt;CallSite, object, object&gt;&gt;.Create(
            Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
                CSharpBinderFlags.None,
                name,
                type,
                new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));

            this.getters.Add(name, get);
        }
    }

    public void Set(string name, object value)
    {
        var set = this.setters[name];
        set.Target(set, this.dyn, value);
    }

    public object Get(string name)
    {
        var get = this.getters[name];
        return get.Target(get, this.dyn);
    }
}
First, a trivial optimization: there is no reason for dyn to be dynamic: all it does is to make the Target invocation dynamic, which here only makes it slower.

On my computer, this makes Get/Set about 25 % faster.



Another possibility is to use Expression instead of CallSite. Since Expression is not as dynamic as dynamic, it can compile to simpler code. For me, using this results in 50 % better speed than your code after applying the previous optimization.

The code:

public class TypeWrapper
{
    private readonly object dyn;
    private readonly Dictionary&lt;string, Action&lt;object, object&gt;&gt; setters
        = new Dictionary&lt;string, Action&lt;object, object&gt;&gt;();

    private readonly Dictionary&lt;string, Func&lt;object, object&gt;&gt; getters
        = new Dictionary&lt;string, Func&lt;object, object&gt;&gt;();

    public TypeWrapper(object d)
    {
        this.dyn = d;
        Type type = d.GetType();
        foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            string name = p.Name;

            var wrappedObjectParameter = Expression.Parameter(typeof(object));
            var valueParameter = Expression.Parameter(typeof(object));

            var setExpression = Expression.Lambda&lt;Action&lt;object, object&gt;&gt;(
                Expression.Assign(
                    Expression.Property(
                        Expression.Convert(wrappedObjectParameter, type), p),
                    Expression.Convert(valueParameter, p.PropertyType)),
                wrappedObjectParameter, valueParameter);

            this.setters.Add(name, setExpression.Compile());

            var getExpression = Expression.Lambda&lt;Func&lt;object, object&gt;&gt;(
                Expression.Convert(
                    Expression.Property(
                        Expression.Convert(wrappedObjectParameter, type), p),
                    typeof(object)),
                wrappedObjectParameter);

            this.getters.Add(name, getExpression.Compile());
        }
    }

    public void Set(string name, object value)
    {
        var set = this.setters[name];
        set(this.dyn, value);
    }

    public object Get(string name)
    {
        var get = this.getters[name];
        return get(this.dyn);
    }
}