Skip to content

Instantly share code, notes, and snippets.

@JimBobSquarePants
Last active February 20, 2020 21:59
Show Gist options
  • Save JimBobSquarePants/f75ac4439eff0eaa22b061c5c4d37c1e to your computer and use it in GitHub Desktop.
Save JimBobSquarePants/f75ac4439eff0eaa22b061c5c4d37c1e to your computer and use it in GitHub Desktop.
You can do really crazy things with C#
public class TypeWrapper
{
private readonly dynamic dyn;
private readonly Dictionary<string, CallSite<Action<CallSite, object, object>>> setters
= new Dictionary<string, CallSite<Action<CallSite, object, object>>>();
private readonly Dictionary<string, CallSite<Func<CallSite, object, object>>> getters
= new Dictionary<string, CallSite<Func<CallSite, 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;
CallSite<Action<CallSite, object, object>> set = CallSite<Action<CallSite, object, object>>.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<Func<CallSite, object, object>> get = CallSite<Func<CallSite, object, object>>.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);
}
}
@JimBobSquarePants
Copy link
Author

JimBobSquarePants commented Apr 26, 2016

Usage

    this.wrapper.Set("Value", "abc");
    return (string)this.wrapper.Get("Value");

Benchmark. 5 and 6 swap position when testing plus I can optimize this to make it faster I think.

// * 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# |   4.6298 ns |  1.9757 ns |   1.00 |
              2. Dynamic C# |  35.0390 ns |  1.2253 ns |   7.57 |
            3. PropertyInfo | 436.9120 ns | 23.1935 ns |  94.37 |
      4. PropertyDescriptor | 954.9500 ns | 17.8619 ns | 206.26 |
 5. PropertyInfoInvocations | 117.4899 ns |  6.8874 ns |  25.38 |
             6. TypeWrapper | 119.8580 ns | 11.1778 ns |  25.89 |

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

1. Static C#: 4.63 ns
2. Dynamic C#: 35.04 ns
3. PropertyInfo: 436.91 ns
4. PropertyDescriptor: 954.95 ns
5. PropertyInfoInvocations: 117.49 ns
6. TypeWrapper: 119.86 ns

// * 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# |   1.8409 ns |  0.3346 ns |   1.00 |
              2. Dynamic C# |  35.8034 ns |  0.4413 ns |  19.45 |
            3. PropertyInfo | 440.2465 ns |  7.2029 ns | 239.15 |
      4. PropertyDescriptor | 946.3422 ns | 12.1229 ns | 514.07 |
 5. PropertyInfoInvocations | 121.2099 ns |  2.4524 ns |  65.84 |
             6. TypeWrapper | 119.3606 ns |  2.1268 ns |  64.84 |

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

1. Static C#: 1.84 ns
2. Dynamic C#: 35.80 ns
3. PropertyInfo: 440.25 ns
4. PropertyDescriptor: 946.34 ns
5. PropertyInfoInvocations: 121.21 ns
6. TypeWrapper: 119.36 ns

@JimBobSquarePants
Copy link
Author

Using concrete IDictionary implementations is definitely consistently faster than using the interface. Timings for Revision 4.

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. PropertyInfoInvocations | 122.2341 ns |  7.5601 ns |  48.12 |
             6. 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. PropertyInfoInvocations: 122.23 ns
6. TypeWrapper: 117.05 ns

@MatthewOverall
Copy link

How can you make this work for nested properties?

@jaypaw549
Copy link

jaypaw549 commented Feb 20, 2020

Way old, but it might be faster if you change the dyn field to an object instead of dynamic. That way it doesn't compile like this.

EDIT: Note the use of dynamic binding in your Get and Set functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment