HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Runtime compiler for getting/setting runtime property values

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
runtimegettingsettingpropertyforcompilervalues

Problem

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>> setters
= new Dictionary>>();

private readonly Dictionary>> getters
= new Dictionary>>();

public TypeWrapper(object d)
{
this.dyn = d;
Type type = d.GetType();
foreach (var p in typ

Solution

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> setters
        = new Dictionary>();

    private readonly Dictionary> getters
        = new Dictionary>();

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

Code Snippets

public class TypeWrapper
{
    private readonly object dyn;
    private readonly Dictionary<string, Action<object, object>> setters
        = new Dictionary<string, Action<object, object>>();

    private readonly Dictionary<string, Func<object, object>> getters
        = new Dictionary<string, Func<object, object>>();

    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<Action<object, object>>(
                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<Func<object, object>>(
                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);
    }
}

Context

StackExchange Code Review Q#126819, answer score: 11

Revisions (0)

No revisions yet.