C# 将 .net Func<T> 转换为 .net Expression<Func<T>>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/767733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
converting a .net Func<T> to a .net Expression<Func<T>>
提问by Dave Cameron
Going from a lambda to an Expression is easy using a method call...
使用方法调用从 lambda 到表达式很容易......
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
But I would like to turn the Func in to an expression, only in rare cases...
但我想把 Func 变成一个表达式,只有在极少数情况下......
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
The line that does not work gives me the compile-time error Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
. An explicit cast does not resolve the situation. Is there a facility to do this that I am overlooking?
不起作用的行给了我编译时错误Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
。显式转换不能解决这种情况。有没有设施可以做到这一点,我正在俯瞰?
采纳答案by Mehrdad Afshari
Ooh, it's not easy at all. Func<T>
represents a generic delegate
and not an expression. If there's any way you could do so (due to optimizations and other things done by the compiler, some data might be thrown away, so it might be impossible to get the original expression back), it'd be disassembling the IL on the fly and inferring the expression (which is by no means easy). Treating lambda expressions as data (Expression<Func<T>>
) is a magic done by the compiler(basically the compiler builds an expression tree in code instead of compiling it to IL).
呵呵,一点都不简单。Func<T>
表示泛型delegate
而不是表达式。如果有任何方法可以这样做(由于编译器所做的优化和其他事情,可能会丢弃一些数据,因此可能无法恢复原始表达式),那就是即时反汇编 IL并推断表达式(这绝非易事)。将 lambda 表达式视为数据 ( Expression<Func<T>>
) 是编译器所做的魔术(基本上,编译器在代码中构建表达式树,而不是将其编译为 IL)。
Related fact
相关事实
This is why languages that push lambdas to the extreme (like Lisp) are often easier to implement as interpreters. In those languages, code and data are essentially the same thing (even at run time), but our chip cannot understand that form of code, so we have to emulate such a machine by building an interpreter on top of it that understands it (the choice made by Lisp like languages) or sacrificing the power (code will no longer be exactly equal to data) to some extent (the choice made by C#). In C#, the compiler gives the illusion of treating code as data by allowing lambdas to be interpreted as code(Func<T>
) and data(Expression<Func<T>>
) at compile time.
这就是将 lambdas 推向极致的语言(如 Lisp)通常更容易实现为解释器的原因。在这些语言中,代码和数据本质上是一回事(即使在运行时),但我们的芯片无法理解这种形式的代码,所以我们必须通过在它上面构建一个理解它的解释器来模拟这样的机器( Lisp 像语言一样做出的选择)或在某种程度上牺牲了功能(代码将不再完全等于数据)(C# 做出的选择)。在 C# 中,编译器通过允许在编译时将 lambda 解释为代码( Func<T>
) 和数据( Expression<Func<T>>
) ,给人一种将代码视为数据的错觉。
回答by Marc Gravell
If you sometimes need an expression and sometimes need a delegate, you have 2 options:
如果您有时需要表达式而有时需要委托,则有两种选择:
- have different methods (1 for each)
- always accept the
Expression<...>
version, and just.Compile().Invoke(...)
it if you want a delegate. Obviously this has cost.
- 有不同的方法(每个方法1个)
- 总是接受
Expression<...>
版本,.Compile().Invoke(...)
如果你想要一个委托,就接受它。显然这是有代价的。
回答by David Wengier
What you probably should do, is turn the method around. Take in an Expression>, and compile and run. If it fails, you already have the Expression to look into.
您可能应该做的是扭转方法。输入一个 Expression>,然后编译并运行。如果失败,则您已经有了要查看的表达式。
public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
try
{
dangerousCall().Compile().Invoke();;
}
catch (Exception e)
{
// This next line does not work...
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
Obviously you need to consider the performance implications of this, and determine if it is something that you really need to do.
显然,您需要考虑这对性能的影响,并确定它是否是您真正需要做的事情。
回答by Steve Willcock
You can go the other way via the .Compile() method however - not sure if this is useful for you:
但是,您可以通过 .Compile() 方法走另一条路 - 不确定这对您是否有用:
public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
try
{
var expr = dangerousCall.Compile();
expr.Invoke();
}
catch (Exception e)
{
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
var thing = new Thing();
ContainTheDanger(() => thing.CrossTheStreams());
}
回答by aaguiar
JB Evain from the Cecil Mono team is doing some progress to enable this
来自 Cecil Mono 团队的 JB Evain 正在取得一些进展以实现这一目标
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
回答by Override
private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)
{
return x => f(x);
}
回答by Sagi
NJection.LambdaConverteris a library that converts delegates to expression
NJection.LambdaConverter是一个将委托转换为表达式的库
public class Program
{
private static void Main(string[] args) {
var lambda = Lambda.TransformMethodTo<Func<string, int>>()
.From(() => Parse)
.ToLambda();
}
public static int Parse(string value) {
return int.Parse(value)
}
}
回答by Dmitry Dzygin
Expression<Func<T>> ToExpression<T>(Func<T> call)
{
MethodCallExpression methodCall = call.Target == null
? Expression.Call(call.Method)
: Expression.Call(Expression.Constant(call.Target), call.Method);
return Expression.Lambda<Func<T>>(methodCall);
}
回答by mheyman
Change
改变
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
To
到
// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();