Как сгенерировать скомпилированную лямбду с вызовами методов?

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


person TheSoftwareJedi    schedule 26.11.2008    source источник


Ответы (2)


Можете ли вы проиллюстрировать, что (если это был обычный 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();
person Marc Gravell    schedule 26.11.2008
comment
Идеально. Работает как шарм. Я столкнулся с проблемой приведения, но вернулся сюда и увидел этот ответ... Спасибо! - person TheSoftwareJedi; 26.11.2008

Вам нужно обернуть объект в 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();
}
person Barry Kelly    schedule 26.11.2008
comment
Выражение.Константа — FTW. Спасибо. Я попробую сейчас и присудить победу, если это хорошо. Спасибо! - person TheSoftwareJedi; 26.11.2008
comment
вы пропустили мой вложенный вызов доступа к членам, но это было достаточно легко добавить в дерево. Спасибо еще раз - person TheSoftwareJedi; 26.11.2008
comment
Я не пропустил это - я поверил, что увидел вашу дилемму и обратился к ней конкретно :) Я работаю над компиляторами в своей повседневной работе, так что это было довольно ясно. - person Barry Kelly; 26.11.2008
comment
Вы пропустили актерский состав, что оставляет меня разделенным между тем, чтобы дать вам ответ, или Марком ... Учитывая, что он включил в себя и актерский состав, звонок и доступ к участникам, я собираюсь бросить их ему. Ваше здоровье. - person TheSoftwareJedi; 26.11.2008