|
using System; |
|
using System.Diagnostics; |
|
using System.Linq.Expressions; |
|
using System.Reflection; |
|
using System.Reflection.Emit; |
|
|
|
namespace PropertyAccessTest |
|
{ |
|
internal static class Program |
|
{ |
|
private static void Benchmark(object obj, PropertyInfo prop, Func<object, object> accessor, string name) |
|
{ |
|
var stopWatch = Stopwatch.StartNew(); |
|
for (var i = 0; i < 10000000; i++) |
|
{ |
|
var result = accessor(obj); |
|
} |
|
stopWatch.Stop(); |
|
Console.WriteLine($"{name}: {stopWatch.ElapsedMilliseconds}ms."); |
|
} |
|
|
|
private static Func<object, object> ByReflection(PropertyInfo prop) |
|
=> o => prop.GetGetMethod().Invoke(o, new object[] { }); |
|
|
|
private static Func<object, object> ByCompile(PropertyInfo prop) |
|
{ |
|
var param = Expression.Parameter(typeof(object)); |
|
var exp = Expression.Lambda<Func<object, object>>( |
|
Expression.Convert( |
|
Expression.Property( |
|
Expression.Convert(param, prop.DeclaringType), |
|
prop |
|
), |
|
typeof(object) |
|
), |
|
param |
|
); |
|
return exp.Compile(); |
|
} |
|
|
|
private static Func<object, object> Converter<I, O>(Delegate func) |
|
=> o => ((Func<I, O>)func)((I)o); |
|
|
|
private static Func<object, object> ByCreateDelegate(PropertyInfo prop) |
|
{ |
|
var converter = typeof(Program) |
|
.GetMethod(nameof(Converter), BindingFlags.Static | BindingFlags.NonPublic) |
|
.MakeGenericMethod(prop.DeclaringType, prop.PropertyType) |
|
.CreateDelegate<Func<Delegate, Func<object, object>>>(); |
|
|
|
var accessor = prop |
|
.GetGetMethod() |
|
.CreateDelegate(typeof(Func<,>).MakeGenericType(prop.DeclaringType, prop.PropertyType)); |
|
|
|
return converter(accessor); |
|
} |
|
|
|
private static Func<object, object> ByEmit(PropertyInfo prop) |
|
{ |
|
var method = new DynamicMethod("GetProperty", typeof(object), new[] { typeof(object) }); |
|
var il = method.GetILGenerator(); |
|
var get = prop.GetGetMethod(); |
|
il.Emit(OpCodes.Ldarg_0); |
|
il.Emit(get.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, get); |
|
il.Emit(OpCodes.Ret); |
|
return method.CreateDelegate<Func<object, object>>(); |
|
} |
|
|
|
private record Testee(int Value); |
|
|
|
internal static void Main(string[] args) |
|
{ |
|
var testee = new Testee(1); |
|
var prop = typeof(Testee).GetProperty(nameof(Testee.Value)); |
|
Benchmark(testee, prop, ByReflection(prop), "By Reflection"); |
|
Benchmark(testee, prop, ByCompile(prop), "By Compile"); |
|
Benchmark(testee, prop, ByCreateDelegate(prop), "By CreateDelegate"); |
|
Benchmark(testee, prop, ByEmit(prop), "By Emit"); |
|
} |
|
} |
|
} |