Лямбда-выражения <T, Func <TIN, TOUT >> и MethodInfo
При переносе проекта с VS2010 на VS2012 я столкнулся со следующей проблемой. Проект много использует Reflection, и для получения 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, но methodCallExpression.Arguments.Count() было 2, если код скомпилирован с VS2012 с целью.Net 4.0.
После декомпиляции я заметил, что компилятор генерирует другой код для одного и того же выражения.
Это проблема дизайна, потому что дизайн не должен ретранслировать "магические числа", такие как число 2 в methodCallExpression.Arguments[2]. Я попытался найти решение для этого, используя следующее:
MethodCallExpression outermostExpression = expression .Body as MethodCallExpression;
MethodInfo myMethod = outermostExpression.Method;
Но externalmostExpression является нулевым.
Наконец, я заставил его работать, изменив выражение следующим образом:
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
Если вы предпочитаете жестко закодированный индекс, вы можете использовать его вместо этого. Также необходима более полная проверка ошибок.