Last active
February 20, 2020 21:59
-
-
Save JimBobSquarePants/f75ac4439eff0eaa22b061c5c4d37c1e to your computer and use it in GitHub Desktop.
You can do really crazy things with C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
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
How can you make this work for nested properties?
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
Usage
Benchmark. 5 and 6 swap position when testing plus I can optimize this to make it faster I think.