Лямбда-выражения> и MethodInfo


При переносе проекта с VS2010 на VS2012 я столкнулся со следующей проблемой. Проект использует отражение много, и для того, чтобы получить MethodInfo из интерфейса был помещен следующий код:

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;

Это прекрасно сработало при компиляции с VS2010, но методcallexpression.Аргументы.Count () был равен 2, если код компилируется с VS2012, нацеленным на .Net 4.0.

После декомпиляции я заметил, что компилятор генерирует другой код для одного и того же выражения.

Это проблема дизайна, потому что дизайн не должен ретранслировать "магические числа", как число 2 в methodCallExpression.Аргументы[2]. Я попытался найти решение для этого, используя следующее:

MethodCallExpression outermostExpression = expression .Body as MethodCallExpression;
MethodInfo myMethod = outermostExpression.Method;

Но внешнее выражение равно нулю.

Наконец, я заставил его работать, изменив выражение следующим образом:

Expression<Func<ITest, ServiceResponse>> expression = scv => scv.Get(default(ServiceRequest));
MethodCallExpression outermostExpression = expression.Body as MethodCallExpression;
Assert.AreEqual("Get", outermostExpression.Method.Name);
Он не идеален, но работает как на VS2010, так и на VS2012.

Есть ли способ найти MethodInfo из выражения, подобного следующее:

Expression<Func<ITest, ServiceResponse>> expression = scv => scv.Get(default(ServiceRequest));
MethodInfo methodInfo = GetInnerMethodInfo( expression );
Assert.AreEqual("Get", methodInfo.Name);
1 8

1 ответ:

Я не уверен, почему существует разница в способе компиляции выражений. Но, если вы ищете информацию о методе метода в константном делегате, вы можете скомпилировать выражение с реализацией ITest, чтобы получить делегаты MethodInfo. Например:

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;

..где Test - некоторый класс и реализует ITest. Который работает как в 2012, так и в 2010 году.

Я не уверен, как вы можете получить эту информацию о методе из выражения в 2012 году без предварительной компиляции оно...

Обновление:

Если компиляция выражения не является опцией, то создается впечатление, что компилятор генерирует другое выражение и помещает свойство MethodInfo в свойство MethodCallExpression.Object компилятора C# 5. Вы можете проверить, не является ли это свойство null и использовать его значение для MethodInfo, или продолжить получение элемента в коллекции Arguments. Например:

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;
}

Я использую запрос LINQ, чтобы получить элемент в коллекции Arguments, если вы предпочитаете жестко закодированный индекс, вы могли бы использовать его вместо этого. Также необходима более полная проверка ошибок.