Lambda expressions <T, Func<TIN,TOUT>> and MethodInfo

c#, lambda, methodinfo, reflection, visual-studio-2012

Solution

I'm not sure why there's a difference in the way the expressions compile. But, if you're looking for the method info of the method in the constant delegate, you can compile the expression with an `ITest` implementation to get at the delegates `MethodInfo`. 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`. Which works in both 2012 and 2010.

I'm unsure how you can get that method info from the expression in 2012 without first compiling it...

UPDATE:

If compiling the expression isn't an option, it appears that the compiler is generating a different expression and putting the `MethodInfo` in the `MethodCallExpression.Object` property in the C# 5 compiler. You can check to see if that property is not `null` and use its value for the `MethodInfo`, or continue on getting an element in the `Arguments` collection. 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'm using the LINQ query to get at the element in the `Arguments` collection, if you prefer the hard-coded index, you could probably use that instead. More complete error-checking is necessary as well.

Problem

While migrating a project from VS2010 to VS2012, I ran into the following problem. The project is using Reflection a lot, and in order to get the MethodInfo from an interface the following code was placed: ``` 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 compiled with VS2010, but the methodCallExpression.Arguments.Count() was 2 if the code is compiled with VS2012 aiming .Net 4.0. After decompiling, I noticed that the compiler was generating different code for the same expression. This is a design problem because the design should not relay on "magic numbers" like the number 2 on methodCallExpression.Arguments[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 it work changing the expression as follow: ``` 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 ideal, but it works on both VS2010 and VS2012. Is there a way to find the 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); ```

Original source