C# 为什么要使用表达式<;Func<;T>&燃气轮机;而不是Func<;T>;?

C# 为什么要使用表达式<;Func<;T>&燃气轮机;而不是Func<;T>;?,c#,delegates,lambda,expression-trees,C#,Delegates,Lambda,Expression Trees,我理解lambdas和Func和操作委托。但是表情 难倒我 当您希望将lambda表达式视为表达式树并查看它们内部而不是执行它们时,在什么情况下会使用表达式而不是普通的Func?。例如,LINQtoSQL获取表达式并将其转换为等效的SQL语句,然后将其提交给服务器(而不是执行lambda) 从概念上讲,Expression与Func完全不同Func表示一个委托,它实际上是一个指向方法的指针,表达式表示lambda表达式的树数据结构。此树结构描述lambda表达式的功能,而不是实际操作。它基本上保

我理解lambdas和
Func
操作
委托。但是表情 难倒我


当您希望将lambda表达式视为表达式树并查看它们内部而不是执行它们时,在什么情况下会使用
表达式而不是普通的
Func

。例如,LINQtoSQL获取表达式并将其转换为等效的SQL语句,然后将其提交给服务器(而不是执行lambda)

从概念上讲,
Expression
Func
完全不同
Func
表示一个
委托
,它实际上是一个指向方法的指针,
表达式
表示lambda表达式的树数据结构。此树结构描述lambda表达式的功能,而不是实际操作。它基本上保存有关表达式、变量、方法调用等组成的数据。。。(例如,它保存诸如lambda是某个常量+某个参数之类的信息)。您可以使用此描述将其转换为实际方法(使用
Expression.Compile
)或使用它执行其他操作(如LINQ to SQL示例)。将lambda视为匿名方法和表达式树的行为纯粹是编译时的事情

Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
Func myFunc=()=>10;//类似于:int myAnonMethod(){return 10;}
将有效地编译为一个IL方法,该方法不获取任何内容,并返回10

Expression<Func<int>> myExpression = () => 10;
Expression myExpression=()=>10;
将转换为一个数据结构,该结构描述了一个不获取参数并返回值10的表达式:


虽然它们在编译时看起来是一样的,但编译器生成的内容却完全不同

当您希望将函数视为数据而不是代码时,可以使用表达式。如果希望操作代码(作为数据),可以这样做。大多数情况下,如果您认为不需要表达式,那么您可能不需要使用表达式。

LINQ是一个典型的例子(例如,与数据库交谈),但事实上,任何时候您都更关心表达要做什么,而不是实际做什么。例如,我在的RPC堆栈中使用此方法(以避免代码生成等),因此您可以使用以下方法调用方法:

string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
这将解构表达式树以解析
SomeMethod
(以及每个参数的值),执行RPC调用,更新任何
ref
/
out
参数,并返回远程调用的结果。这只能通过表达式树实现。我会更多地报道这个


另一个例子是,当您手动构建表达式树以编译为lambda时,正如代码所做的那样。

在选择表达式vs Func时,一个极其重要的考虑因素是像LINQ to Entities这样的IQueryable提供程序可以“消化”您在表达式中传递的内容,但将忽略在Func中传递的内容。我有两篇关于这个主题的博文:


(最后一节)

我想补充一些关于
Func
Expression
之间区别的注释:

  • Func
    只是一个普通的老式多播代理
  • 表达式
    是lambda表达式的表达式树表示形式
  • 表达式树可以通过lambda表达式语法或API语法构造
  • 表达式树可以编译为委托
    Func
  • 逆转换理论上是可能的,但它是一种反编译,没有内置的功能,因为它不是一个简单的过程
  • 表达式树可以通过
    ExpressionVisitor
    进行观察/翻译/修改
  • IEnumerable的扩展方法使用
    Func
  • IQueryable的扩展方法使用
    表达式
    进行操作
有一篇文章用代码示例描述了细节:


希望对您有所帮助。

主要原因是您不想直接运行代码,而是想检查代码。这可能有多种原因:

  • 将代码映射到不同的环境(即实体框架中的C#代码到SQL)
  • 在运行时替换部分代码(动态编程甚至纯干式技术)
  • 代码验证(在模拟脚本或进行分析时非常有用)
  • 序列化-表达式可以非常容易和安全地序列化,而委托不能
  • 强类型安全,即使在运行时执行动态调用,也可以利用编译器检查(ASP.NET MVC 5 with Razor就是一个很好的例子)

    • 我为noobs添加了一个答案,因为这些答案似乎让我不知所措,直到我意识到这是多么简单。有时候,正是你对事情复杂的期待让你无法“把脑袋绕在它周围”

      我不需要理解其中的区别,直到我遇到了一个非常讨厌的“bug”,试图通用地使用LINQ to SQL:

      public IEnumerable<T> Get(Func<T, bool> conditionLambda){
        using(var db = new DbContext()){
          return db.Set<T>.Where(conditionLambda);
        }
      }
      
      public IEnumerable Get(Func conditionLambda){
      使用(var db=new DbContext()){
      返回db.Set.Where(conditionLambda);
      }
      }
      
      在我开始在更大的数据集上处理内存异常之前,这种方法非常有效。在lambda中设置断点使我意识到它正在逐个遍历表中的每一行,以查找与lambda条件匹配的内容。这让我困惑了一段时间,因为为什么它要把我的数据表当作一个巨大的IEnumerable,而不是像它应该做的那样执行LINQ到SQL?在我的LINQ to MongoDb中,它也做了完全相同的事情

      修复方法就是将
      Func
      转换成
      表达式,所以我在谷歌上搜索它为什么需要一个<