Lambda expressions <T, Func <TIN, TOUT >> and MethodInfo
When moving the project from VS2010 to VS2012, I encountered the following problem. The project uses Reflection a lot, and in order to get MethodInfo from the interface, the following code was added:
Expression<Func<ITest, Func<ServiceRequest, ServiceResponse>>> expression = scv => scv.Get; UnaryExpression unaryExpression = expression.Body as UnaryExpression; MethodCallExpression methodCallExpression = unaryExpression.Operand as MethodCallExpression; ConstantExpression constantExpression = methodCallExpression.Arguments[2] as ConstantExpression; MethodInfo myMethod = constantExpression.Value as MethodInfo; This worked fine with VS2010, but the CallExpression.Arguments.Count () method was 2 if the code was compiled with VS2012 targeting .Net 4.0.
After decompiling, I noticed that the compiler generates different code for the same expression.
This is a design problem, because the design should not relay "magic numbers", for example, number 2 using the CallExpression.Arguments method [2]. I tried to find a solution for this using the following:
MethodCallExpression outermostExpression = expression .Body as MethodCallExpression; MethodInfo myMethod = outermostExpression.Method; But outermostExpression is null.
Finally, I made him change the expression as follows:
Expression<Func<ITest, ServiceResponse>> expression = scv => scv.Get(default(ServiceRequest)); MethodCallExpression outermostExpression = expression.Body as MethodCallExpression; Assert.AreEqual("Get", outermostExpression.Method.Name); It is not perfect, but it works on both VS2010 and VS2012.
Is there a way to find MethodInfo from an expression like the following:
Expression<Func<ITest, ServiceResponse>> expression = scv => scv.Get(default(ServiceRequest)); MethodInfo methodInfo = GetInnerMethodInfo( expression ); Assert.AreEqual("Get", methodInfo.Name); I'm not sure why there is a difference in how expressions are compiled. But if you are looking for information about a method method in constant deletes, you can compile the expression with the ITest implementation to get MethodInfo delegates. For example:
Expression<Func<ITest, Func<ServiceRequest, ServiceResponse>>> expression = scv => new Func<ServiceRequest, ServiceResponse>(scv.Get); Func<ServiceRequest, ServiceResponse> ret = expression.Compile()(new Test()); MethodInfo methodInfo = ret.Method; .. where Test is some class and implements ITest . This works in both 2012 and 2010.
I'm not sure how you can get this method information from an expression in 2012 without first compiling ...
UPDATE:
If compiling the expression is not an option, it looks like the compiler generates another expression and puts MethodInfo in the MethodInfo property in the C # 5 compiler. You can check if this property is null and use its value for MethodInfo , or continue to get the element in Arguments collections. For example:
Expression<Func<ITest, Func<ServiceRequest, ServiceResponse>>> expression = scv => new Func<ServiceRequest, ServiceResponse>(scv.Get); UnaryExpression unaryExpression = expression.Body as UnaryExpression; MethodCallExpression methodCallExpression = unaryExpression.Operand as MethodCallExpression; ConstantExpression constantExpression = methodCallExpression.Object as ConstantExpression; MethodInfo methodInfo; if (constantExpression != null) { methodInfo = constantExpression.Value as MethodInfo; } else { constantExpression = methodCallExpression.Arguments .Single(a => a.Type == typeof(MethodInfo) && a.NodeType == ExpressionType.Constant) as ConstantExpression; methodInfo = constantExpression.Value as MethodInfo; } I use the LINQ query to get an item in the Arguments collection, if you prefer a hard-coded index, you could probably use that. A more complete error checking is also necessary.