Reflection and model factory
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -7,22 +6,64 @@ namespace Umbraco.Core
|
||||
internal static class ExpressionExtensions
|
||||
{
|
||||
public static Expression<Func<T, bool>> True<T>() { return f => true; }
|
||||
|
||||
public static Expression<Func<T, bool>> False<T>() { return f => false; }
|
||||
|
||||
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left,
|
||||
Expression<Func<T, bool>> right)
|
||||
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
|
||||
{
|
||||
var invokedExpr = Expression.Invoke(right, left.Parameters.Cast<Expression>());
|
||||
return Expression.Lambda<Func<T, bool>>
|
||||
(Expression.OrElse(left.Body, invokedExpr), left.Parameters);
|
||||
var invokedExpr = Expression.Invoke(right, left.Parameters);
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left.Body, invokedExpr), left.Parameters);
|
||||
}
|
||||
|
||||
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left,
|
||||
Expression<Func<T, bool>> right)
|
||||
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
|
||||
{
|
||||
var invokedExpr = Expression.Invoke(right, left.Parameters.Cast<Expression>());
|
||||
return Expression.Lambda<Func<T, bool>>
|
||||
(Expression.AndAlso(left.Body, invokedExpr), left.Parameters);
|
||||
var invokedExpr = Expression.Invoke(right, left.Parameters);
|
||||
return Expression.Lambda<Func<T, bool>> (Expression.AndAlso(left.Body, invokedExpr), left.Parameters);
|
||||
}
|
||||
|
||||
public static Action CompileToDelegate(this Expression<Action> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Action<T1> CompileToDelegate<T1>(this Expression<Action<T1>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Action<T1, T2> CompileToDelegate<T1, T2>(this Expression<Action<T1, T2>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Action<T1, T2, T3> CompileToDelegate<T1, T2, T3>(this Expression<Action<T1, T2, T3>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Func<TResult> CompileToDelegate<TResult>(this Expression<Func<TResult>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Func<T1, TResult> CompileToDelegate<T1, TResult>(this Expression<Func<T1, TResult>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Func<T1, T2, TResult> CompileToDelegate<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static Func<T1, T2, T3, TResult> CompileToDelegate<T1, T2, T3, TResult>(this Expression<Func<T1, T2, T3, TResult>> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
|
||||
public static TMethod CompileToDelegate<TMethod>(this Expression<TMethod> expr)
|
||||
{
|
||||
return ReflectionUtilities.CompileToDelegate(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
@@ -39,6 +40,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
var ctorArgTypes = new[] { typeof(IPublishedElement) };
|
||||
var modelInfos = new Dictionary<string, ModelInfo>(StringComparer.InvariantCultureIgnoreCase);
|
||||
var exprs = new List<Expression<Func<IPublishedElement, IPublishedElement>>>();
|
||||
|
||||
ModelTypeMap = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
@@ -68,31 +70,16 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
if (modelInfos.TryGetValue(typeName, out ModelInfo modelInfo))
|
||||
throw new InvalidOperationException($"Both types {type.FullName} and {modelInfo.ModelType.FullName} want to be a model type for content type with alias \"{typeName}\".");
|
||||
|
||||
// see Umbraco.Tests.Benchmarks.CtorInvokeBenchmarks
|
||||
// using ctor.Invoke is horrible, cannot even consider it,
|
||||
// then expressions are 6-10x slower than direct ctor, and
|
||||
// dynamic methods are 2-3x slower than direct ctor = best
|
||||
|
||||
// much faster with a dynamic method but potential MediumTrust issues - which we don't support
|
||||
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
|
||||
var meth = new DynamicMethod(string.Empty, typeof(IPublishedElement), ctorArgTypes, type.Module, true);
|
||||
var gen = meth.GetILGenerator();
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var func = (Func<IPublishedElement, IPublishedElement>) meth.CreateDelegate(typeof (Func<IPublishedElement, IPublishedElement>));
|
||||
|
||||
// fast enough and works in MediumTrust - but we don't
|
||||
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
|
||||
//var exprArg = Expression.Parameter(typeof(IPropertySet), "content");
|
||||
//var exprNew = Expression.New(constructor, exprArg);
|
||||
//var expr = Expression.Lambda<Func<IPropertySet, IPropertySet>>(exprNew, exprArg);
|
||||
//var func = expr.Compile();
|
||||
|
||||
modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, Ctor = func, ModelType = type };
|
||||
exprs.Add(Expression.Lambda<Func<IPublishedElement, IPublishedElement>>(Expression.New(constructor)));
|
||||
modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, ModelType = type };
|
||||
ModelTypeMap[typeName] = type;
|
||||
}
|
||||
|
||||
var compiled = ReflectionUtilities.CompileToDelegates(exprs.ToArray());
|
||||
var i = 0;
|
||||
foreach (var modelInfo in modelInfos.Values)
|
||||
modelInfo.Ctor = compiled[i++];
|
||||
|
||||
_modelInfos = modelInfos.Count > 0 ? modelInfos : null;
|
||||
}
|
||||
|
||||
|
||||
572
src/Umbraco.Core/ReflectionUtilities.cs
Normal file
572
src/Umbraco.Core/ReflectionUtilities.cs
Normal file
@@ -0,0 +1,572 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utilities to simplify reflection.
|
||||
/// </summary>
|
||||
public static class ReflectionUtilities
|
||||
{
|
||||
private static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(PropertyInfo property)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
|
||||
var getMethod = property.GetMethod;
|
||||
if (getMethod == null)
|
||||
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a getter.");
|
||||
|
||||
var exprThis = Expression.Parameter(type, "this");
|
||||
var exprCall = Expression.Call(exprThis, getMethod);
|
||||
var expr = Expression.Lambda<Func<TInstance, TValue>>(exprCall, exprThis);
|
||||
return expr.CompileToDelegate();
|
||||
}
|
||||
|
||||
private static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(PropertyInfo property)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
|
||||
var setMethod = property.SetMethod;
|
||||
if (setMethod == null)
|
||||
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a setter.");
|
||||
|
||||
var exprThis = Expression.Parameter(type, "this");
|
||||
var exprArg0 = Expression.Parameter(typeof(TValue), "value");
|
||||
var exprCall = Expression.Call(exprThis, setMethod, exprArg0);
|
||||
var expr = Expression.Lambda<Action<TInstance, TValue>>(exprCall, exprThis, exprArg0);
|
||||
return expr.CompileToDelegate();
|
||||
}
|
||||
|
||||
public static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(string propertyName)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
var type0 = typeof(TValue);
|
||||
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (property == null || property.PropertyType != type0)
|
||||
throw new InvalidOperationException($"Could not get property {type}.{propertyName} : {type0}.");
|
||||
return GetPropertyGetter<TInstance, TValue>(property);
|
||||
}
|
||||
|
||||
public static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(string propertyName)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
var type0 = typeof(TValue);
|
||||
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (property == null || property.PropertyType != type0)
|
||||
throw new InvalidOperationException($"Could not get property {type}.{propertyName} : {type0}.");
|
||||
return GetPropertySetter<TInstance, TValue>(property);
|
||||
}
|
||||
|
||||
public static void GetPropertyGetterSetter<TInstance, TValue>(string propertyName, out Func<TInstance, TValue> getter, out Action<TInstance, TValue> setter)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
var type0 = typeof(TValue);
|
||||
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (property == null || property.PropertyType != type0)
|
||||
throw new InvalidOperationException($"Could not get property {type}.{propertyName} : {type0}.");
|
||||
getter = GetPropertyGetter<TInstance, TValue>(property);
|
||||
setter = GetPropertySetter<TInstance, TValue>(property);
|
||||
}
|
||||
|
||||
public static Func<TInstance> GetCtor<TInstance>()
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
|
||||
// get the constructor infos
|
||||
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
|
||||
null, Type.EmptyTypes, null);
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
|
||||
|
||||
var exprNew = Expression.New(ctor);
|
||||
var expr = Expression.Lambda<Func<TInstance>>(exprNew);
|
||||
return expr.CompileToDelegate();
|
||||
}
|
||||
|
||||
public static Func<TArg0, TInstance> GetCtor<TInstance, TArg0>()
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
var type0 = typeof(TArg0);
|
||||
|
||||
// get the constructor infos
|
||||
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
|
||||
null, new[] { type0 }, null);
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException($"Could not find constructor {type}.ctor({type0}).");
|
||||
|
||||
var exprArgs = ctor.GetParameters().Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray();
|
||||
// ReSharper disable once CoVariantArrayConversion
|
||||
var exprNew = Expression.New(ctor, exprArgs);
|
||||
var expr = Expression.Lambda<Func<TArg0, TInstance>>(exprNew, exprArgs);
|
||||
return expr.CompileToDelegate();
|
||||
}
|
||||
|
||||
public static Func<TArg0, TArg1, TInstance> GetCtor<TInstance, TArg0, TArg1>()
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
var type0 = typeof(TArg0);
|
||||
var type1 = typeof(TArg1);
|
||||
|
||||
// get the constructor infos
|
||||
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
|
||||
null, new[] { type0, type1 }, null);
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException($"Could not find constructor {type}.ctor({type0}, {type1}).");
|
||||
|
||||
var exprArgs = ctor.GetParameters().Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray();
|
||||
// ReSharper disable once CoVariantArrayConversion
|
||||
var exprNew = Expression.New(ctor, exprArgs);
|
||||
var expr = Expression.Lambda<Func<TArg0, TArg1, TInstance>>(exprNew, exprArgs);
|
||||
return expr.CompileToDelegate();
|
||||
}
|
||||
|
||||
public static TMethod GetMethod<TMethod>(MethodInfo method)
|
||||
{
|
||||
var type = method.DeclaringType;
|
||||
|
||||
GetMethodParms<TMethod>(out var parameterTypes, out var returnType);
|
||||
return GetStaticMethod<TMethod>(method, method.Name, type, parameterTypes, returnType);
|
||||
}
|
||||
|
||||
public static TMethod GetMethod<TInstance, TMethod>(MethodInfo method)
|
||||
{
|
||||
var type = method.DeclaringType;
|
||||
|
||||
GetMethodParms<TInstance, TMethod>(out var parameterTypes, out var returnType);
|
||||
return GetMethod<TMethod>(method, method.Name, type, parameterTypes, returnType);
|
||||
}
|
||||
|
||||
public static TMethod GetMethod<TInstance, TMethod>(string methodName)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
|
||||
GetMethodParms<TInstance, TMethod>(out var parameterTypes, out var returnType);
|
||||
|
||||
var method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
|
||||
null, parameterTypes, null);
|
||||
|
||||
return GetMethod<TMethod>(method, methodName, type, parameterTypes, returnType);
|
||||
}
|
||||
|
||||
private static void GetMethodParms<TMethod>(out Type[] parameterTypes, out Type returnType)
|
||||
{
|
||||
var typeM = typeof(TMethod);
|
||||
var typeList = new List<Type>();
|
||||
returnType = typeof(void);
|
||||
|
||||
if (!typeof(MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof(MulticastDelegate))
|
||||
throw new InvalidOperationException("Invalid TMethod, must be a Func or Action.");
|
||||
|
||||
var typeName = typeM.FullName;
|
||||
if (typeName.StartsWith("System.Func`"))
|
||||
{
|
||||
var i = 0;
|
||||
while (i < typeM.GenericTypeArguments.Length - 1)
|
||||
typeList.Add(typeM.GenericTypeArguments[i++]);
|
||||
returnType = typeM.GenericTypeArguments[i];
|
||||
}
|
||||
else if (typeName.StartsWith("System.Action`"))
|
||||
{
|
||||
var i = 0;
|
||||
while (i < typeM.GenericTypeArguments.Length)
|
||||
typeList.Add(typeM.GenericTypeArguments[i++]);
|
||||
}
|
||||
else if (typeName == "System.Action")
|
||||
{
|
||||
// no args
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException(typeName);
|
||||
|
||||
parameterTypes = typeList.ToArray();
|
||||
}
|
||||
|
||||
private static void GetMethodParms<TInstance, TMethod>(out Type[] parameterTypes, out Type returnType)
|
||||
{
|
||||
var type = typeof(TInstance);
|
||||
|
||||
var typeM = typeof(TMethod);
|
||||
var typeList = new List<Type>();
|
||||
returnType = typeof(void);
|
||||
|
||||
if (!typeof(MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof(MulticastDelegate))
|
||||
throw new InvalidOperationException("Invalid TMethod, must be a Func or Action.");
|
||||
|
||||
var typeName = typeM.FullName;
|
||||
if (!typeM.IsGenericType)
|
||||
throw new InvalidOperationException(typeName);
|
||||
if (typeM.GenericTypeArguments[0] != type)
|
||||
throw new InvalidOperationException("Invalid TMethod, the first generic argument must be TInstance.");
|
||||
if (typeName.StartsWith("System.Func`"))
|
||||
{
|
||||
var i = 1;
|
||||
while (i < typeM.GenericTypeArguments.Length - 1)
|
||||
typeList.Add(typeM.GenericTypeArguments[i++]);
|
||||
returnType = typeM.GenericTypeArguments[i];
|
||||
}
|
||||
else if (typeName.StartsWith("System.Action`"))
|
||||
{
|
||||
var i = 1;
|
||||
while (i < typeM.GenericTypeArguments.Length)
|
||||
typeList.Add(typeM.GenericTypeArguments[i++]);
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException(typeName);
|
||||
|
||||
parameterTypes = typeList.ToArray();
|
||||
}
|
||||
|
||||
private static TMethod GetStaticMethod<TMethod>(MethodInfo method, string methodName, Type type, Type[] parameterTypes, Type returnType)
|
||||
{
|
||||
if (method == null || method.ReturnType != returnType)
|
||||
throw new InvalidOperationException($"Could not find static method {type}.{methodName}({string.Join(",", parameterTypes.Select(x => x.ToString()))}) : {returnType}");
|
||||
|
||||
var e = new List<ParameterExpression>();
|
||||
foreach (var p in method.GetParameters())
|
||||
e.Add(Expression.Parameter(p.ParameterType, p.Name));
|
||||
var exprCallArgs = e.ToArray();
|
||||
var exprLambdaArgs = exprCallArgs;
|
||||
|
||||
// ReSharper disable once CoVariantArrayConversion
|
||||
var exprCall = Expression.Call(method, exprCallArgs);
|
||||
var expr = Expression.Lambda<TMethod>(exprCall, exprLambdaArgs);
|
||||
return expr.CompileToDelegate();
|
||||
}
|
||||
|
||||
private static TMethod GetMethod<TMethod>(MethodInfo method, string methodName, Type type, Type[] parameterTypes, Type returnType)
|
||||
{
|
||||
if (method == null || method.ReturnType != returnType)
|
||||
throw new InvalidOperationException($"Could not find method {type}.{methodName}({string.Join(",", parameterTypes.Select(x => x.ToString()))}) : {returnType}");
|
||||
|
||||
var e = new List<ParameterExpression>();
|
||||
foreach (var p in method.GetParameters())
|
||||
e.Add(Expression.Parameter(p.ParameterType, p.Name));
|
||||
var exprCallArgs = e.ToArray();
|
||||
|
||||
var exprThis = Expression.Parameter(type, "this");
|
||||
e.Insert(0, exprThis);
|
||||
var exprLambdaArgs = e.ToArray();
|
||||
|
||||
// ReSharper disable once CoVariantArrayConversion
|
||||
var exprCall = Expression.Call(exprThis, method, exprCallArgs);
|
||||
var expr = Expression.Lambda<TMethod>(exprCall, exprLambdaArgs);
|
||||
return expr.Compile();
|
||||
}
|
||||
|
||||
public static object GetStaticProperty(this Type type, string propertyName, Func<IEnumerable<PropertyInfo>, PropertyInfo> filter = null)
|
||||
{
|
||||
var propertyInfo = GetPropertyInfo(type, propertyName, filter);
|
||||
if (propertyInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(propertyName),
|
||||
$"Couldn't find property {propertyName} in type {type.FullName}");
|
||||
return propertyInfo.GetValue(null, null);
|
||||
}
|
||||
|
||||
public static object CallStaticMethod(this Type type, string methodName, params object[] parameters)
|
||||
{
|
||||
var methodInfo = GetMethodInfo(type, methodName);
|
||||
if (methodInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(methodName),
|
||||
$"Couldn't find method {methodName} in type {type.FullName}");
|
||||
return methodInfo.Invoke(null, parameters);
|
||||
}
|
||||
|
||||
public static object CallMethod(this object obj, string methodName, params object[] parameters)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
Type type = obj.GetType();
|
||||
var methodInfo = GetMethodInfo(type, methodName);
|
||||
if (methodInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(methodName),
|
||||
$"Couldn't find method {methodName} in type {type.FullName}");
|
||||
return methodInfo.Invoke(obj, parameters);
|
||||
}
|
||||
|
||||
public static object CallMethod(this object obj, string methodName, Func<IEnumerable<MethodInfo>, MethodInfo> filter = null, params object[] parameters)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
Type type = obj.GetType();
|
||||
var methodInfo = GetMethodInfo(type, methodName, filter);
|
||||
if (methodInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(methodName),
|
||||
$"Couldn't find method {methodName} in type {type.FullName}");
|
||||
return methodInfo.Invoke(obj, parameters);
|
||||
}
|
||||
|
||||
private static MethodInfo GetMethodInfo(Type type, string methodName, Func<IEnumerable<MethodInfo>, MethodInfo> filter = null)
|
||||
{
|
||||
MethodInfo methodInfo;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
methodInfo = type.GetMethod(methodName,
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
}
|
||||
catch (AmbiguousMatchException)
|
||||
{
|
||||
if (filter == null) throw;
|
||||
|
||||
methodInfo = filter(
|
||||
type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
|
||||
.Where(x => x.Name == methodName));
|
||||
}
|
||||
type = type.BaseType;
|
||||
}
|
||||
while (methodInfo == null && type != null);
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
private static PropertyInfo GetPropertyInfo(Type type, string propertyName, Func<IEnumerable<PropertyInfo>, PropertyInfo> filter = null)
|
||||
{
|
||||
PropertyInfo propInfo;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
propInfo = type.GetProperty(propertyName,
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
}
|
||||
catch (AmbiguousMatchException)
|
||||
{
|
||||
if (filter == null) throw;
|
||||
|
||||
propInfo = filter(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
|
||||
.Where(x => x.Name == propertyName));
|
||||
}
|
||||
type = type.BaseType;
|
||||
}
|
||||
while (propInfo == null && type != null);
|
||||
return propInfo;
|
||||
}
|
||||
|
||||
public static object GetPropertyValue(this object obj, string propertyName)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
Type objType = obj.GetType();
|
||||
PropertyInfo propInfo = GetPropertyInfo(objType, propertyName);
|
||||
if (propInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(propertyName),
|
||||
$"Couldn't find property {propertyName} in type {objType.FullName}");
|
||||
return propInfo.GetValue(obj, null);
|
||||
}
|
||||
|
||||
public static object GetPropertyValue(this object obj, string propertyName, IDictionary<string, PropertyInfo> propCache)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
Type objType = obj.GetType();
|
||||
PropertyInfo propInfo;
|
||||
if (propCache.ContainsKey(propertyName))
|
||||
{
|
||||
propInfo = propCache[propertyName];
|
||||
}
|
||||
else
|
||||
{
|
||||
propInfo = GetPropertyInfo(objType, propertyName);
|
||||
if (propInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(propertyName),
|
||||
$"Couldn't find property {propertyName} in type {objType.FullName}");
|
||||
|
||||
propCache[propertyName] = propInfo;
|
||||
}
|
||||
return propInfo.GetValue(obj, null);
|
||||
}
|
||||
|
||||
public static void SetPropertyValue(this object obj, string propertyName, object val)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
Type objType = obj.GetType();
|
||||
PropertyInfo propInfo = GetPropertyInfo(objType, propertyName);
|
||||
if (propInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(propertyName),
|
||||
$"Couldn't find property {propertyName} in type {objType.FullName}");
|
||||
propInfo.SetValue(obj, val, null);
|
||||
}
|
||||
|
||||
public static void SetPropertyValue(this object obj, string propertyName, object val, IDictionary<string, PropertyInfo> propCache)
|
||||
{
|
||||
if (obj == null) throw new ArgumentNullException(nameof(obj));
|
||||
if (propCache == null) throw new ArgumentNullException(nameof(propCache));
|
||||
|
||||
Type objType = obj.GetType();
|
||||
PropertyInfo propInfo;
|
||||
if (propCache.ContainsKey(propertyName))
|
||||
{
|
||||
propInfo = propCache[propertyName];
|
||||
}
|
||||
else
|
||||
{
|
||||
propInfo = GetPropertyInfo(objType, propertyName);
|
||||
if (propInfo == null)
|
||||
throw new ArgumentOutOfRangeException(nameof(propertyName),
|
||||
$"Couldn't find property {propertyName} in type {objType.FullName}");
|
||||
|
||||
propCache[propertyName] = propInfo;
|
||||
}
|
||||
|
||||
propInfo.SetValue(obj, val, null);
|
||||
}
|
||||
|
||||
public static Action CompileToDelegate(Expression<Action> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static); // CompileToMethod requires a static method
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Action) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Action<T1> CompileToDelegate<T1>(Expression<Action<T1>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof (void), new[] { typeof (T1) });
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Action<T1>) Delegate.CreateDelegate(typeof (Action<T1>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Action<T1, T2> CompileToDelegate<T1, T2>(Expression<Action<T1, T2>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof (void), new[] { typeof(T1), typeof (T2) });
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Action<T1, T2>) Delegate.CreateDelegate(typeof (Action<T1, T2>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Action<T1, T2, T3> CompileToDelegate<T1, T2, T3>(Expression<Action<T1, T2, T3>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof (void), new[] { typeof (T1), typeof (T2) , typeof (T3) });
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Action<T1, T2, T3>) Delegate.CreateDelegate(typeof (Action<T1, T2, T3>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Func<TResult> CompileToDelegate<TResult>(Expression<Func<TResult>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static,
|
||||
typeof (TResult), Array.Empty<Type>()); // CompileToMethod requires a static method
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Func<TResult>) Delegate.CreateDelegate(typeof (Func<TResult>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Func<T1, TResult> CompileToDelegate<T1, TResult>(Expression<Func<T1, TResult>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof (TResult), new[] { typeof (T1) });
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Func<T1,TResult>) Delegate.CreateDelegate(typeof (Func<T1, TResult>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Func<T1, T2, TResult> CompileToDelegate<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof(TResult), new[] { typeof (T1), typeof(T2) });
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Func<T1, T2, TResult>) Delegate.CreateDelegate(typeof (Func<T1, T2, TResult>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static Func<T1, T2, T3, TResult> CompileToDelegate<T1, T2, T3, TResult>(Expression<Func<T1, T2, T3, TResult>> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof (TResult), new[] { typeof (T1), typeof (T2), typeof (T3) });
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (Func<T1, T2, T3, TResult>) Delegate.CreateDelegate(typeof (Func<T1, T2, T3, TResult>), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static TMethod CompileToDelegate<TMethod>(Expression<TMethod> expr)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
GetMethodParms<TMethod>(out var parameterTypes, out var returnType);
|
||||
|
||||
var builder = typeBuilder.DefineMethod("Method",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
returnType, parameterTypes);
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
|
||||
return (TMethod) (object) Delegate.CreateDelegate(typeof (TMethod), typeBuilder.CreateType().GetMethod("Method"));
|
||||
}
|
||||
|
||||
public static TMethod[] CompileToDelegates<TMethod>(params Expression<TMethod>[] exprs)
|
||||
{
|
||||
var typeBuilder = CreateTypeBuilder();
|
||||
|
||||
GetMethodParms<TMethod>(out var parameterTypes, out var returnType);
|
||||
|
||||
var i = 0;
|
||||
foreach (var expr in exprs)
|
||||
{
|
||||
var builder = typeBuilder.DefineMethod($"Method_{i++}",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
returnType, parameterTypes);
|
||||
|
||||
expr.CompileToMethod(builder);
|
||||
}
|
||||
|
||||
var type = typeBuilder.CreateType();
|
||||
|
||||
var methods = new TMethod[exprs.Length];
|
||||
for (i = 0; i < exprs.Length; i++)
|
||||
methods[i] = (TMethod) (object) Delegate.CreateDelegate(typeof (TMethod), type.GetMethod($"Method_{i++}"));
|
||||
return methods;
|
||||
}
|
||||
|
||||
private static TypeBuilder CreateTypeBuilder()
|
||||
{
|
||||
var assemblyName = new AssemblyName("Umbraco.Core.DynamicAssemblies." + Guid.NewGuid().ToString("N"));
|
||||
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
|
||||
var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
|
||||
return module.DefineType("Class", TypeAttributes.Public | TypeAttributes.Abstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Release\Umbraco.Core.xml</DocumentationFile>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@@ -1280,6 +1281,7 @@
|
||||
<Compile Include="PropertyEditors\ValueValidatorAttribute.cs" />
|
||||
<Compile Include="Publishing\ScheduledPublisher.cs" />
|
||||
<Compile Include="ReadLock.cs" />
|
||||
<Compile Include="ReflectionUtilities.cs" />
|
||||
<Compile Include="RenderingEngine.cs" />
|
||||
<Compile Include="RuntimeLevel.cs" />
|
||||
<Compile Include="RuntimeState.cs" />
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.Horology;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests.Benchmarks
|
||||
{
|
||||
@@ -28,16 +25,19 @@ namespace Umbraco.Tests.Benchmarks
|
||||
// see benchmarkdotnet FAQ
|
||||
Add(Job.Default
|
||||
.WithLaunchCount(1) // benchmark process will be launched only once
|
||||
.WithIterationTime(TimeInterval.FromMilliseconds(100)) // 100ms per iteration
|
||||
.WithWarmupCount(3) // 3 warmup iteration
|
||||
.WithTargetCount(3)); // 3 target iteration
|
||||
.WithIterationTime(TimeInterval.FromMilliseconds(400))
|
||||
.WithWarmupCount(3)
|
||||
.WithTargetCount(6));
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IFoo _foo = new Foo(null);
|
||||
private ConstructorInfo _ctorInfo;
|
||||
private Func<IFoo, IFoo> _dynamicMethod;
|
||||
private Func<IFoo, IFoo> _expression;
|
||||
private IFoo _foo = new Foo(null);
|
||||
private Func<IFoo, IFoo> _expressionMethod;
|
||||
private Func<IFoo, IFoo> _expressionMethod2;
|
||||
private Func<IFoo, IFoo> _expressionMethod3;
|
||||
private Func<IFoo, IFoo> _expressionMethod4;
|
||||
|
||||
[Setup]
|
||||
public void Setup()
|
||||
@@ -55,18 +55,91 @@ namespace Umbraco.Tests.Benchmarks
|
||||
//IL_000b: pop
|
||||
//IL_000c: ret
|
||||
|
||||
var meth = new DynamicMethod(string.Empty, typeof(Foo), ctorArgTypes, type.Module, true);
|
||||
// generate a dynamic method
|
||||
//
|
||||
// ldarg.0 // obj0
|
||||
// newobj instance void [Umbraco.Tests.Benchmarks]Umbraco.Tests.Benchmarks.CtorInvokeBenchmarks / Foo::.ctor(class [Umbraco.Tests.Benchmarks] Umbraco.Tests.Benchmarks.CtorInvokeBenchmarks/IFoo)
|
||||
// ret
|
||||
|
||||
var meth = new DynamicMethod(string.Empty, typeof (IFoo), ctorArgTypes, type.Module, true);
|
||||
var gen = meth.GetILGenerator();
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
//gen.Emit(OpCodes.Call, constructor);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
_dynamicMethod = (Func<IFoo, IFoo>) meth.CreateDelegate(typeof(Func<IFoo, IFoo>));
|
||||
_dynamicMethod = (Func<IFoo, IFoo>) meth.CreateDelegate(typeof (Func<IFoo, IFoo>));
|
||||
|
||||
var exprArg = Expression.Parameter(typeof(IFoo), "content");
|
||||
// generate a compiled expression
|
||||
//
|
||||
// ldarg.0 // content
|
||||
// newobj instance void [Umbraco.Tests.Benchmarks]Umbraco.Tests.Benchmarks.CtorInvokeBenchmarks / Foo::.ctor(class [Umbraco.Tests.Benchmarks] Umbraco.Tests.Benchmarks.CtorInvokeBenchmarks/IFoo)
|
||||
// ret
|
||||
|
||||
var exprArg = Expression.Parameter(typeof (IFoo), "content");
|
||||
var exprNew = Expression.New(constructor, exprArg);
|
||||
var expr = Expression.Lambda<Func<IFoo, IFoo>>(exprNew, exprArg);
|
||||
_expression = expr.Compile();
|
||||
_expressionMethod = expr.Compile();
|
||||
|
||||
// create a dynamic assembly
|
||||
// dump to disk so we can review IL code with eg DotPeek
|
||||
|
||||
var assemblyName = new AssemblyName("Umbraco.Tests.Benchmarks.IL");
|
||||
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
|
||||
var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
|
||||
var typeBuilder = module.DefineType("CtorInvoke", TypeAttributes.Public | TypeAttributes.Abstract);
|
||||
|
||||
var expressionMethodBuilder = typeBuilder.DefineMethod("ExpressionCtor",
|
||||
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
|
||||
typeof (IFoo), ctorArgTypes);
|
||||
expr.CompileToMethod(expressionMethodBuilder);
|
||||
|
||||
var dynamicMethodBuilder = typeBuilder.DefineMethod("DynamicCtor",
|
||||
MethodAttributes.Public | MethodAttributes.Static,
|
||||
typeof(IFoo), ctorArgTypes);
|
||||
gen = dynamicMethodBuilder.GetILGenerator();
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
meth.CreateDelegate(typeof (Func<IFoo, IFoo>));
|
||||
|
||||
var btype = typeBuilder.CreateType(); // need to build before saving
|
||||
assembly.Save("Umbraco.Tests.Benchmarks.IL.dll");
|
||||
|
||||
// at that point,
|
||||
// _dynamicMethod is 2x slower than direct ctor
|
||||
// _expressionMethod is 6x slower than direct ctor
|
||||
// which is weird as inspecting the assembly IL shows that they are generated
|
||||
// exactly the same, and yet it is confirmed eg by https://stackoverflow.com/questions/4211418
|
||||
//
|
||||
// not sure why exactly
|
||||
// see https://stackoverflow.com/questions/13431573
|
||||
// see http://mattwarren.org/2017/01/25/How-do-.NET-delegates-work/#different-types-of-delegates
|
||||
//
|
||||
// note that all the benchmark methods have the very same IL code so it's
|
||||
// really the 'callvirt ...' that ends up doing different things
|
||||
//
|
||||
// more readings:
|
||||
// http://byterot.blogspot.dk/2012/05/performance-comparison-of-code.html
|
||||
// https://stackoverflow.com/questions/1296683
|
||||
// https://stackoverflow.com/questions/44239127
|
||||
// that last one points to
|
||||
// https://blogs.msdn.microsoft.com/seteplia/2017/02/01/dissecting-the-new-constraint-in-c-a-perfect-example-of-a-leaky-abstraction/
|
||||
// which reads ... "Expression.Compile creates a DynamicMethod and associates it with an anonymous assembly
|
||||
// to run it in a sandboxed environment. This makes it safe for a dynamic method to be emitted and executed
|
||||
// by partially trusted code but adds some run-time overhead."
|
||||
// and, turning things into a delegate (below) removes that overhead...
|
||||
|
||||
// turning it into a delegate seems cool, _expressionMethod2 is ~ _dynamicMethod
|
||||
_expressionMethod2 = (Func<IFoo, IFoo>) Delegate.CreateDelegate(typeof (Func<IFoo, IFoo>), btype.GetMethod("ExpressionCtor"));
|
||||
|
||||
// nope, this won't work, throws an ArgumentException because 'MethodInfo must be a MethodInfo object'
|
||||
// and here it's of type System.Reflection.Emit.DynamicMethod+RTDynamicMethod - whereas the btype one is ok
|
||||
// so, the dynamic assembly step is required
|
||||
//
|
||||
//_expressionMethod3 = (Func<IFoo, IFoo>) Delegate.CreateDelegate(typeof (Func<IFoo, IFoo>), _expressionMethod.Method);
|
||||
|
||||
// but, our utilities know how to do it!
|
||||
_expressionMethod3 = expr.CompileToDelegate();
|
||||
_expressionMethod4 = ReflectionUtilities.GetCtor<Foo, IFoo>();
|
||||
}
|
||||
|
||||
public IFoo IlCtor(IFoo foo)
|
||||
@@ -74,7 +147,7 @@ namespace Umbraco.Tests.Benchmarks
|
||||
return new Foo(foo);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[Benchmark(Baseline = true)]
|
||||
public void DirectCtor()
|
||||
{
|
||||
var foo = new Foo(_foo);
|
||||
@@ -95,7 +168,25 @@ namespace Umbraco.Tests.Benchmarks
|
||||
[Benchmark]
|
||||
public void ExpressionCtor()
|
||||
{
|
||||
var foo = _expression(_foo);
|
||||
var foo = _expressionMethod(_foo);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Expression2Ctor()
|
||||
{
|
||||
var foo = _expressionMethod2(_foo);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Expression3Ctor()
|
||||
{
|
||||
var foo = _expressionMethod3(_foo);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Expression4Ctor()
|
||||
{
|
||||
var foo = _expressionMethod4(_foo);
|
||||
}
|
||||
|
||||
public interface IFoo
|
||||
|
||||
Reference in New Issue
Block a user