Как мне скомпилировать лямбду с вызовами методов?
-
11-07-2019 - |
Вопрос
Я генерирую скомпилированные методы получения во время выполнения для данного члена. Прямо сейчас мой код предполагает, что результатом метода get является строка (хорошо сработала для тестирования). Тем не менее, я бы хотел, чтобы это работало с написанным мной собственным классом конвертера, см. Ниже "ConverterBase" ссылка, которую я добавил.
Я не могу понять, как добавить вызов к классу конвертера в мое дерево выражений.
public Func<U, string> GetGetter<U>(MemberInfo info)
{
Type t = null;
if (info is PropertyInfo)
{
t = ((PropertyInfo)info).PropertyType;
}
else if (info is FieldInfo)
{
t = ((FieldInfo)info).FieldType;
}
else
{
throw new Exception("Unknown member type");
}
//TODO, replace with ability to specify in custom attribute
ConverterBase typeConverter = new ConverterBase();
ParameterExpression target = Expression.Parameter(typeof(U), "target");
MemberExpression memberAccess = Expression.MakeMemberAccess(target, info);
//TODO here, make the expression call "typeConverter.FieldToString(fieldValue)"
LambdaExpression getter = Expression.Lambda(memberAccess, target);
return (Func<U, string>)getter.Compile();
}
Я ищу что поместить во вторую область TODO (я могу обработать первую :)).
Результирующая скомпилированная лямбда-функция должна принимать экземпляр типа U в качестве параметра, вызывать указанную функцию доступа к члену, а затем вызывать конвертер FieldToString " метод с результатом и вернуть полученную строку.
Решение
Можете ли вы проиллюстрировать, что (если это был обычный C #) вы хотите, чтобы выражение оценивалось? Я могу написать выражение достаточно легко - я просто не до конца понимаю вопрос ...
(отредактируйте комментарий) - в этом случае это будет что-то вроде:
ConverterBase typeConverter = new ConverterBase();
var target = Expression.Parameter(typeof(U), "target");
var getter = Expression.MakeMemberAccess(target, info);
var converter = Expression.Constant(typeConverter, typeof(ConverterBase));
return Expression.Lambda<Func<U, string>>(
Expression.Call(converter, typeof(ConverterBase).GetMethod("FieldToString"),
getter), target).Compile();
Или, если тип отказывается от привязки, вам нужно будет ввести приведение / преобразование:
MethodInfo method = typeof(ConverterBase).GetMethod("FieldToString");
return Expression.Lambda<Func<U, string>>(
Expression.Call(converter, method,
Expression.Convert(getter, method.GetParameters().Single().ParameterType)),
target).Compile();
Другие советы
Вам нужно обернуть объект в ExpressionConstant, например, используя Expression.Constant. Вот пример:
class MyConverter
{
public string MyToString(int x)
{
return x.ToString();
}
}
static void Main()
{
MyConverter c = new MyConverter();
ParameterExpression p = Expression.Parameter(typeof(int), "p");
LambdaExpression intToStr = Expression.Lambda(
Expression.Call(
Expression.Constant(c),
c.GetType().GetMethod("MyToString"),
p),
p);
Func<int,string> f = (Func<int,string>) intToStr.Compile();
Console.WriteLine(f(42));
Console.ReadLine();
}