动态获取 Func<T,bool> 调用的结果

3

我正在尝试根据特定条件序列化某些内容。

为此,我最初希望在对象的属性上使用包含lambda表达式的属性。

然而,由于这是不可能的,我已经决定在类中使用Func<T,bool>成员,并通过属性的类型(或第一个参数类型)和名称传递此Func。例如:

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

在我的序列化程序中,我需要调用这个 Func。
假设在这种情况下,我有一个类型t,它等于typeof(SomeObject),或者更抽象地说,是typeof(T)。我也可以获取Func本身,但只能通过反射作为对象来实现。
我的天真方法大致如下:
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 */

但是这会导致将lambda表达式转换为委托的问题,显然是错误的。

我尝试了在'func'的位置使用这个:

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

但这取决于func是一个方法调用而不是lambda表达式。那么,有人能指点我方向吗?
3个回答

4
您可以像这样做,无需使用表达式:

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);

我有一个类型变量,而不是实际的类型,你不能使用 (Predicate<sometypevariable>)a.GetValue()。你还能采用这种方法吗? - Matt Mitchell
是的,请看我的编辑,使用委托转换和 DynamicInvoke。 - Jan Jongboom
太棒了!谢谢 :-)。看起来是最简单的解决方案,正在测试中。 - Matt Mitchell
感谢您的代码。它真的帮助我解决了我的问题。 - thangchung

1

不确定这是否是有效的示例,但这就是方法:

// 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);

嗯,看起来可能会起作用,但请记住我不了解SomeObject,我只有一个代表它的类型变量。因此,在最后一行中,我不能使用Expression.Lambda<Func<SomeObject, Boolean>>。但是非泛型Lambda方法可能能够帮助我。 - Matt Mitchell
我也试图避免创建一个完整的方法来包装Func,因为原始问题是尽可能轻松地装饰现有属性。 - Matt Mitchell

1

我认为您可以使用Lambda表达式的Compile方法将其转换为委托。

以下是我在MSDN上找到的内容:

Expression<(Of <(TDelegate>)>)类型提供了Compile方法,该方法将由表达式树表示的代码编译为可执行委托。这个可执行代码等同于最初将Lambda表达式分配给委托类型时生成的可执行代码。

在这里您可以找到它。


是的,谢谢。但我需要一个表达式才能编译它。现在,我不能创建这个表达式。 - Matt Mitchell
啊,但我在那里看到一个例子,我可以简单地使用Expression<Func<SomeObject, bool>> = (lambda)代替(Func<SomeObject, bool>>>)。我只需确保在我的对象类中引用Expressions而不是Funcs。可惜我找不到一种动态转换这两者的方法。 - Matt Mitchell
Func<T, T>不是委托吗?为什么要将其转换为Lambda表达式? - Beatles1692
如果有更好的方法来获取类型为object的Func的求值结果,我很乐意知道。 - Matt Mitchell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接