C# 转换.net函数<;T>;到.net表达式<;Func<;T>&燃气轮机;
使用方法调用从lambda转换为表达式很容易C# 转换.net函数<;T>;到.net表达式<;Func<;T>&燃气轮机;,c#,.net,lambda,expression,func,C#,.net,Lambda,Expression,Func,使用方法调用从lambda转换为表达式很容易 public void GimmeExpression(Expression<Func<T>> expression) { ((MemberExpression)expression.Body).Member.Name; // "DoStuff" } public void SomewhereElse() { GimmeExpression(() => thing.DoStuff()); } publ
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
public void GimmeExpression(表达式)
{
((MemberExpression)expression.Body).Member.Name;/“DoStuff”
}
公共空间在别处无效()
{
GimmeExpression(()=>thing.DoStuff());
}
但是我想把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());
}
public void ContainTheDanger(Func-dangerousCall)
{
尝试
{
危险呼叫();
}
捕获(例外e)
{
//下一行行不通。。。
表达式危险表达式=危险调用;
变量nameOfDanger=
((MemberExpression)dangerousCall.Body).Member.Name;
抛出新的危险容器(
“危险出现时”+危险名称,e);
}
}
公共空间在别处无效()
{
包含危险(()=>thing.CrossTheStreams());
}
不起作用的行给出了编译时错误
无法将类型“System.Func”隐式转换为“System.Linq.Expressions.Expression”
。显式强制转换不能解决这种情况。有没有我可以忽略的设施来做这件事?哦,这一点都不容易Func
表示泛型委托
,而不是表达式。如果有任何方法可以做到这一点(由于编译器进行了优化和其他工作,一些数据可能会被丢弃,因此可能无法恢复原始表达式),那么就需要动态分解IL并推断表达式(这绝非易事)。将lambda表达式视为数据(表达式
)是编译器完成的一个魔术(基本上,编译器在代码中构建表达式树,而不是将其编译为IL)
相关事实
这就是为什么将lambda推向极端的语言(如Lisp)通常更容易作为解释器实现的原因。在这些语言中,代码和数据本质上是相同的(即使在运行时),但我们的芯片无法理解这种形式的代码,因此我们必须通过在机器上构建一个能够理解它的解释器(类似Lisp语言的选择)或牺牲能力(代码将不再完全等于数据)来模拟这种机器在某种程度上(C#所做的选择)。在C#中,编译器通过允许lambda在编译时被解释为code(
Func
)和数据(Expression
),给人一种将代码视为数据的错觉。如果有时需要表达式,有时需要委托,则有两个选项:
- 有不同的方法(每个方法1个)
- 始终接受
版本,如果需要委托,只需表达式
即可。显然,这是有代价的.Compile().Invoke(…)
- 您可能应该做的是改变方法。接受表达式>,然后编译并运行。如果失败,那么您已经有了要研究的表达式
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());
}
public void ContainTheDanger(表达式危险呼叫)
{
尝试
{
危险调用().Compile().Invoke();;
}
捕获(例外e)
{
//下一行行不通。。。
变量nameOfDanger=
((MemberExpression)dangerousCall.Body).Member.Name;
抛出新的危险容器(
“危险出现时”+危险名称,e);
}
}
公共空间在别处无效()
{
包含危险(()=>thing.CrossTheStreams());
}
显然,你需要考虑这一点的性能含义,并确定它是否是你真正需要做的事情。
< p>你可以通过.cCulielor()方法走另一条路——但不确定这是否对你有用: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());
}
public void ContainTheDanger(表达式危险呼叫)
{
尝试
{
var expr=dangerousCall.Compile();
expr.Invoke();
}
捕获(例外e)
{
表达式危险表达式=危险调用;
var nameOfDanger=((MethodCallExpression)dangerousCall.Body).Method.Name;
抛出新的危险容器(“危险显现时”+危险名称,e);
}
}
公共空间在别处无效()
{
var thing=新事物();
包含危险(()=>thing.CrossTheStreams());
}
Cecil Mono团队的JB Evain正在为此做一些工作
私有静态表达式FuncToExpression(Func f)
{
返回x=>f(x);
}
是一个将委托转换为表达式的库
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)
}
}
公共类程序
{
私有静态void Main(字符串[]args){
var lambda=lambda.TransformMethodTo()
.From(()=>Parse)
.ToLambda();
}
公共静态int解析(字符串值){
返回int.Parse(值)
}
}
表达式到表达式(Func调用)
{
MethodCallExpression methodCall=call.Target==null
?表达式调用(调用方法)
:Expression.Call(Expression.Constant(Call.Target)、Call.Method);
返回表达式.Lambda(methodCall);
}
更改
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
//下一行行不通。。。
表达式危险表达式=危险调用;
到
//下一行行可以用!
表达式危险表达式=()=>危险调用();
我想遍历返回表达式的语法树。这种方法允许我这样做吗?@DaveCameron-不。请参见上面的答案-已编译的Func
将隐藏在新表达式中。这只是在代码上添加了一层数据;您可以遍历一个层,只需找到参数f
,而无需进一步的详细信息,因此您就可以从这里开始了。宏必须在编译时展开,如果您想支持eval
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);
}
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();