using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Collections.Generic; namespace TheFunctionFactory { /// /// Creates and compiles a Func using expressions /// /// /// Inspired from: /// http://abhi.dcmembers.com/blog/2009/03/25/lambda-based-reflection-vs-normal-reflection-vs-direct-call-4/ /// public static class FunctionFactory { /// /// Creates a compiled delegate function for the specified type and method name /// /// Delegate Func to create /// Constant to get method from /// Method to examine /// Delegate function of the specified methodname public static TFunc CreateFunc(object obj, string methodName) { List args = new List(); Type targetType = obj.GetType(); MethodInfo minfo = targetType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); if (minfo != null) { var target = Expression.Constant(obj); foreach (var arg in minfo.GetParameters()) args.Add(Expression.Parameter(arg.ParameterType, arg.Name)); var methodinvokeExpression = Expression.Call(target, minfo, args.ToArray()); var lambda = Expression.Lambda(methodinvokeExpression, args.ToArray()); //now the following Lambda is created: // (TArg1, TArg2) => obj.MethodName(TArg1, TArg2); return lambda.Compile(); } return default(TFunc); } /// /// Creates a compiled delegate function using expressions, /// the first Func{TObject,TReturn} parameter must be the constant to be passed in /// /// Delegate Func to create /// Type of constant to pass in to the Func /// Method to examine /// Delegate function of the specified methodname /// /// The function Func{TType,TArg1,TArg2} with a method name of "CallMe" would create the following /// lambda: /// /// (TType, TArg1, TArg2) => TType.CallMe(TArg1, TArg2); /// /// public static TFunc CreateFunc(Type targetType, string methodName) { List args = new List(); MethodInfo minfo = targetType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); if (minfo != null) { Type objectType = typeof(TFunc).GetGenericArguments().First(); var targetParam = Expression.Parameter(objectType, "a"); if (!targetType.IsAssignableFrom(objectType)) throw new InvalidCastException(string.Format("{0} cannot be cast to {1}", targetType.Name, objectType.Name)); var target = Expression.Convert(targetParam, targetType); foreach (var arg in minfo.GetParameters()) args.Add(Expression.Parameter(arg.ParameterType, arg.Name)); var methodinvokeExpression = Expression.Call(target, minfo, args.ToArray()); var lambda = Expression.Lambda(methodinvokeExpression, new ParameterExpression[]{targetParam}.Concat(args)); //now the following Lambda is created: // (a, TArg1, TArg2) => a.MethodName(TArg1, TArg2); return lambda.Compile(); } return default(TFunc); } } }