using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; namespace Umbraco.Core { /// /// Provides utilities to simplify reflection. /// public static class ReflectionUtilities { public static Func GetPropertyGetter(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(property); } public static Action GetPropertySetter(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(property); } public static void GetPropertyGetterSetter(string propertyName, out Func getter, out Action 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(property); setter = GetPropertySetter(property); } public static Func GetCtor() { 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>(exprNew); return expr.CompileToDelegate(); } public static Func GetCtor(Type type) { // 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>(exprNew); return expr.CompileToDelegate(); } public static Func GetCtor() { 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>(exprNew, exprArgs); return expr.CompileToDelegate(); } public static Func GetCtor() { 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>(exprNew, exprArgs); return expr.CompileToDelegate(); } public static TMethod GetMethod(MethodInfo method) { var type = method.DeclaringType; GetMethodParms(out var parameterTypes, out var returnType); return GetStaticMethod(method, method.Name, type, parameterTypes, returnType); } public static TMethod GetMethod(MethodInfo method) { var type = method.DeclaringType; GetMethodParms(out var parameterTypes, out var returnType); return GetMethod(method, method.Name, type, parameterTypes, returnType); } public static TMethod GetMethod(string methodName) { var type = typeof(TInstance); GetMethodParms(out var parameterTypes, out var returnType); var method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, parameterTypes, null); return GetMethod(method, methodName, type, parameterTypes, returnType); } private static Func GetPropertyGetter(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>(exprCall, exprThis); return expr.CompileToDelegate(); } private static Action GetPropertySetter(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>(exprCall, exprThis, exprArg0); return expr.CompileToDelegate(); } private static void GetMethodParms(out Type[] parameterTypes, out Type returnType) { var typeM = typeof(TMethod); var typeList = new List(); 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(out Type[] parameterTypes, out Type returnType) { var type = typeof(TInstance); var typeM = typeof(TMethod); var typeList = new List(); 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(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(); 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(exprCall, exprLambdaArgs); return expr.CompileToDelegate(); } private static TMethod GetMethod(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(); 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(exprCall, exprLambdaArgs); return expr.Compile(); } // not sure we want this at all? /* public static object GetStaticProperty(this Type type, string propertyName, Func, 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, 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, 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, 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 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 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 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 CompileToDelegate(Expression> 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) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } public static Action CompileToDelegate(Expression> 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) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } public static Action CompileToDelegate(Expression> 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) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } public static Func CompileToDelegate(Expression> expr) { var typeBuilder = CreateTypeBuilder(); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof (TResult), Array.Empty()); // CompileToMethod requires a static method expr.CompileToMethod(builder); return (Func) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } public static Func CompileToDelegate(Expression> 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) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } public static Func CompileToDelegate(Expression> 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) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } public static Func CompileToDelegate(Expression> 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) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } public static TMethod CompileToDelegate(Expression expr) { var typeBuilder = CreateTypeBuilder(); GetMethodParms(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(params Expression[] exprs) { var typeBuilder = CreateTypeBuilder(); GetMethodParms(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); } } }