Лямбда-выражения> и 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 ответ:
Я не уверен, почему существует разница в способе компиляции выражений. Но, если вы ищете информацию о методе метода в константном делегате, вы можете скомпилировать выражение с реализацией
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
, если вы предпочитаете жестко закодированный индекс, вы могли бы использовать его вместо этого. Также необходима более полная проверка ошибок.