Динамически получать результат вызова Func ‹T, bool›

Я пытаюсь сериализовать что-то на основе определенных критериев.

С этой целью я изначально надеялся использовать атрибуты, содержащие лямбда-выражение для свойств объекта.

Однако, поскольку это невозможно сделать, я решил иметь член Func ‹T, bool> внутри класса и передавать тип (или первый тип параметра) и имя этого Func через атрибут свойства. Например.:

Func<SomeObject, bool> func = (p => p.Value == 4);
[FuncAtt(typeof(SomeObject), "func")]
public SomeObject PropertyName { get; set;}

В моем сериализаторе мне нужно вызвать это Func ‹T, bool>.

Предположим, у меня есть Type t, который в данном случае равен typeof (SomeObject) или, более абстрактно, typeof (T). Я также могу получить сам Func ‹T, bool>, но только через отражение как объект.

Мой наивный подход звучит примерно так:

object func = typeof(MyClass).GetField(attribute.FuncName).GetValue(MyClassInstance);
Type funcType = typeof(Func<,>).MakeGenericType(attribute.Type, typeof(bool));

ParameterExpression p = Expression.Parameter(attribute.Type, objectToSerialize);
LambdaExpression l = Expression.Lambda(funcType, func, p); /* Won't work */

Но это приводит к проблеме приведения лямбды к делегату, что явно ошибочно.

Я пробовал это вместо func:

(Expression)((Action)(() => func))

Но это зависит от того, что func - это вызов метода, а не лямбда.

Итак, может ли кто-нибудь указать мне правильное направление?


person Matt Mitchell    schedule 14.09.2009    source источник


Ответы (3)


Вы можете просто сделать что-то вроде этого, не используя выражения:

public static class Test
{
    public static Predicate<int> func = s => s > 20;
}

и получить значение:

    private void Form1_Load(object sender, EventArgs e)
    {
        var a = typeof(Test).GetField("func");

        bool validates = ((Predicate<int>)a.GetValue(null)).Invoke(100);
    }

изменить, чтобы получить значение, не зная типа:

bool validates = (bool)((Delegate)a.GetValue(null)).DynamicInvoke(100);
person Jan Jongboom    schedule 14.09.2009
comment
У меня есть переменная типа, а не фактический тип, и вы не можете перейти (Predicate ‹sometypevariable›) a.GetValue (). Можете ли вы по-прежнему придерживаться этого подхода? - person Matt Mitchell; 14.09.2009
comment
Да, посмотрите мою правку с приведением к делегированию и DynamicInvoke. - person Jan Jongboom; 14.09.2009
comment
Здорово! Спасибо :-). Смотрится сейчас самое простое решение и тестирование. - person Matt Mitchell; 14.09.2009
comment
Спасибо за ваш код. Это действительно помогло мне разобраться в моих проблемах. - person thangchung; 27.05.2013

Я думаю, вы можете использовать метод Compile лямбда-выражения, чтобы передать его делегату.

вот что я нашел на MSDN:

Тип Expression ‹(Of‹ (TDelegate>)>) предоставляет метод Compile, который компилирует код, представленный деревом выражения, в исполняемый делегат. Этот исполняемый код эквивалентен исполняемому коду, который был бы сгенерирован, если бы лямбда-выражение было изначально назначено типу делегата.

Его можно найти здесь.

person Beatles1692    schedule 14.09.2009
comment
Да, спасибо, но мне нужно выражение, чтобы я мог его скомпилировать. Как бы то ни было, я не могу создать Выражение. - person Matt Mitchell; 14.09.2009
comment
Но я вижу там пример, где я могу просто перейти в Expression ‹Func‹ SomeObject, bool ›› = (lambda) вместо (Func ‹SomeObject, bool ›››). Я просто обязательно буду ссылаться на выражения, а не на функции в своих классах объектов. Жалко, я не могу найти способ преобразовать два динамически. - person Matt Mitchell; 14.09.2009
comment
разве Func ‹T, T› не делегат? почему вы хотите преобразовать его в лямбда-выражение? - person Beatles1692; 14.09.2009
comment
Если есть лучший способ получить результат оценки Func, имея в виду, что он относится к типу object, я бы хотел его знать. - person Matt Mitchell; 14.09.2009

Не уверен, что это рабочий образец, но вот способ:

// not sure what are you doing in this line, but assume it should return
// a method name specified in the attribute, e.g. "func" in your example.
// Also "func" must be a method (static one in my example) of SomeObject class
String funcname = typeof(MyClass).GetField(attribute.FuncName).GetValue(MyClassInstance);
ParameterExpression param = Expression.Parameter(typeof(SomeObject), "p");
MethodCallExpression call = Expression.Call(SomeObject, funcname, new Type[] { typeof(SomeObject), typeof(Boolean) }, param);
LambdaExpression lambda = Expression.Lambda<Func<SomeObject, Boolean>>(call, param);

теперь вы можете вызвать метод func следующим образом:

Boolean result = lambda.Compile()(SomeObjectInstance);
person Kamarey    schedule 14.09.2009
comment
Хм, похоже, это может сработать, но имейте в виду, что я не знаю SomeObject, у меня просто есть переменная типа, представляющая его. Поэтому я не могу в последней строке указать Expression.Lambda ‹Func‹ SomeObject, Boolean ››. Но неуниверсальный метод Lambda может мне помочь. - person Matt Mitchell; 14.09.2009
comment
Я также пытался избежать создания полного метода, обертывающего Func, поскольку исходная проблема заключается в том, чтобы как можно проще украсить существующие свойства. - person Matt Mitchell; 14.09.2009