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

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