从 Func 创建表达式

62

我在我的代码中有一个Func<TCollection, T>,用于选择特定的属性。

在调用另一个方法时,我需要将Expression<Func<TCollection, T>>作为参数。

是否有办法将Func<TCollection, T>转换(或创建)为Expression<Func<TCollection, T>>?

谢谢


1
可能是将 .net Func<T> 转换为 .net Expression<Func<T>>的重复问题。 - GSerg
5个回答

65

你无法基于方法重建表达式,因为表达式需要知道原始语句而不是IL。但是,你可以创建一个表达式,其中调用了你的函数,例如:

Func<int> func = () => 1;
Expression<Func<int>> expression = Expression.Lambda<Func<int>>(Expression.Call(func.Method));

需要注意的是,像EF这样的系统无法真正处理这种情况。


2
假设你能够获取IL,那么你就可以像Reflector和ILSpy一样反编译IL。(当然,不能保证这与编译IL的代码完全相同。)有没有办法获取委托的IL呢? - phoog
6
我认为你需要在调用中添加func.Target。我尝试使用更复杂的Func<>,但是它抱怨我试图使用空对象调用实例方法。我将其更改为Expression.Call(Expression.Constant(func.Target), func.Method, args),然后一切都好了。 - ben
如果正确使用,EF 可以与之配合工作。请参见 https://stackoverflow.com/a/40958545/292787。 - Stanislav

40

虽然你可以创建一个调用委托的表达式树,但这很可能没有什么用 - 因为对于分析表达式树的代码来说,委托基本上是一个黑匣子。假设你想使用类似于LINQ to SQL这样的东西,查询分析器需要能够查看你的逻辑以将其转换为SQL - 如果它到达一个普通的委托,那么它就无法完成这项工作。

你应该改变首次创建委托的代码,改为创建一个表达式树。


34

你可以像这样做:

Func<object, string> func = a => a.ToString();
Expression<Func<object, string>> expr = a => func(a);

但是你只会得到包含原始Func方法调用的表达式,而无法分析Func本身的内容。


4
但是自然而然,Skeet先生比我表达得更好! :-) - Simon
3
你说了这句话,但是你的回答才是让我在从机场疯狂驾车的夜晚过后清醒思考的那个回答……简洁地重新陈述了一个函数是可以被调用的东西,而表达式是对函数的调用。 - Grimm The Opiner

9
您不能从一个委托(从Func<TCollection, T>Expression<Func<TCollection, T>>)中创建表达式,但您可以做相反的操作。

也就是说,通过编译它(.compile),将Expression<Func<TCollection, T>>转换为Func<TCollection, T>

因此,如果您需要两者,可以在函数中使用表达式,并在提供的集合对象上编译和执行它们。

当然,我们必须注意编译表达式会很慢。


这正是我所需要的。我只是试图减少重复的代码。看来我从错误的角度考虑了它。 - Paul Johnson
如果希望避免代码重复,那么这就是正确的解决方案。 - Jesse

1
创建一个如下的类;
 public class GenericFilter<T> where T : class, new()
    {
        public Expression<Func<T, bool>> Filter = item => true;

    }

用法;

var filter=new GenericFilter<blabla>().Filter.And(x=>...);
filter=filter.And(x=>...);

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