【问题标题】:What is the difference between LINQ query expressions and extension methodsLINQ 查询表达式和扩展方法有什么区别
【发布时间】:2010-10-22 05:27:42
【问题描述】:

以下是返回相同数据的两个查询。其他风格我不确定哪个更好。

哪些因素会影响这些查询? 使用一种风格比另一种风格有什么好处?

示例 1

var x = from s in db.Surveys
    join sq in db.Survey_Questions on s.ID equals sq.Survey_ID
    join q in db.Questions on sq.Question_ID equals q.ID
    join qg in db.Question_Groups on q.ID equals qg.Question_ID
    where s.Type_ID.Equals(typeID) & s.Type.Equals(type)
    select new { question = sq.Question, status = sq.Status, grp = qg };

示例 2

var x = db.Surveys.Where(s => s.Type_ID.Equals(typeID) & s.Type.Equals(type))
              .Join(db.Survey_Questions,
                        s => s.ID,
                        sq => sq.Survey_ID,
                        (s, sq) => new
                        {
                            question = sq.Question,
                            status = sq.Status
                        })
              .Join(db.Question_Groups,
                        q => q.question.ID,
                        qg => qg.Question_ID,
                        (q, qg) => new
                        {
                            question = q.question,
                            status = q.status,
                            group = qg
                        }).ToList();

【问题讨论】:

  • 值得一提的是您的样品并不相同...
  • 最后的 .ToList() 强制执行完全不同。将其关闭以使它们等效。第一个 x 是 IQueryable 类型,第二个是 IList.

标签: c# linq query-expressions


【解决方案1】:

更新:你已经修正了你的标题,所以忽略咆哮。

您的问题标题与您的代码示例无关。您的问题暗示一种语法是 IEnumerable,另一种是 IQueryable,但这是不正确的。在您的示例中,如果 db.Surveys 是一个 IQueryable,那么 两个 您的示例都在使用 IQueryable。我将尝试回答两个问题。

您的两个代码示例只是编写相同 LINQ 查询的不同方式(假设它们编写得很好)。示例 1 中的代码只是示例 2 中代码的简写。编译器以相同的方式处理两个示例中的代码。想想 C# 编译器将int?Nullable<System.Int32> 相同的处理方式。 C# 和 VB.Net 语言都提供这种速记查询语法。其他语言可能没有此语法,您必须使用示例 2 语法。事实上,其他语言甚至可能不支持扩展方法或 lambda 表达式,您还必须使用更丑陋的语法。


更新:

进一步以 Sander 为例,当您编写此代码时(查询理解语法):

var surveyNames = from s in db.Surveys select s.Name

认为编译器将这种简写方式变成了这个(扩展方法和 lambda 表达式):

IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);

但实际上扩展方法和 lambda 表达式本身就是简写。编译器会发出这样的东西(不完全是,只是为了给出一个想法):

Expression<Func<Survey, string>> selector = delegate(Survey s) { return s.Name; };
IQueryable<string> surveryNames = Queryable.Select(db.Surveys, selector);

注意Select() 只是Queryable 类中的一个静态方法。如果您的 .NET 语言不支持查询语法、lambda 或扩展方法,那您就不得不自己编写代码了。


使用一种风格比另一种风格有什么好处?

对于小型查询,扩展方法可以更紧凑:

var items = source.Where(s => s > 5);

另外,扩展方法语法可以更加灵活,比如条件where子句:

var items = source.Where(s => s > 5);

if(smallerThanThen)
    items = items.Where(s => s < 10);
if(even)
    items = items.Where(s => (s % 2) == 0);

return items.OrderBy(s => s);

此外,有几种方法只能通过扩展方法语法使用(Count()、Aggregate()、Take()、Skip()、ToList()、ToArray() 等),所以如果我要使用一个其中,我通常会用这种语法编写整个查询,以避免混合两种语法。

var floridaCount = source.Count(s => s.State == "FL");

var items = source
            .Where(s => s > 5)
            .Skip(5)
            .Take(3)
            .ToList();

另一方面,当查询变得更大更复杂时,查询理解语法会更清晰,尤其是当您开始使用一些 letgroupjoin 等变得复杂时。

最后,我通常会使用对每个特定查询效果更好的那个。


更新:你修正了你的标题,所以忽略其余的......

现在,关于您的标题:关于 LINQ,IEnumerable 和 IQueryable 非常相似。它们都具有几乎相同的扩展方法(Select、Where、Count 等),主要(唯一?)区别在于 IEnumerable 将 Func&lt;TIn,TOut&gt; 作为参数,而 IQueryable 将 Expression&lt;Func&lt;TIn,TOut&gt;&gt; 作为参数。两者的表达方式相同(通常是lamba 表达式),但在内部它们完全不同。

IEnumerable 是 LINQ to Objects 的入口。 LINQ to Objects 扩展方法可以在任何 IEnumerable(数组、列表、可以使用 foreach 迭代的任何内容)上调用,Func&lt;TIn,TOut&gt; 在编译时转换为 IL 并在运行时像普通方法代码一样运行。请注意,其他一些 LINQ 提供程序使用 IEnumerable,因此实际上在幕后使用 LINQ to Objects(LINQ to XML、LINQ to DataSet)。

IQueryable 由 LINQ to SQL、LINQ to Entities 和其他 LINQ 提供程序使用,这些提供程序需要检查您的查询并对其进行转换,而不是直接执行您的代码。 IQueryable 查询及其Expression&lt;Func&lt;TIn,TOut&gt;&gt;s 不会在编译时编译到 IL 中。而是创建了一个表达式树,并且可以在运行时进行检查。这允许将语句翻译成其他查询语言(例如 T-SQL)。表达式树可以在运行时编译成 Func 并在需要时执行。

可以在this question 中找到说明差异的示例,其中 OP 希望在 SQL Server 中执行部分 LINQ to SQL 查询,将对象放入托管代码中,然后在 LINQ 中执行其余查询以对象。为了实现这一点,他所要做的就是将 IQueryable 转换为他希望发生切换的 IEnumerable。

【讨论】:

    【解决方案2】:

    LINQ 是一种技术的流行词。

    IQueryable 是 LINQ 使用的 .NET 接口。

    除了风格,两者没有区别。使用您喜欢的样式。

    我更喜欢第一种样式用于长语句(如此处所示),第二种用于非常短的语句。

    【讨论】:

      【解决方案3】:

      第一个示例中的 where 子句实际上只是第二种方法中 Where 子句的语法糖。事实上,您可以编写自己的与 Linq 或 IQueryable 无关的类,只需使用 Where 方法,您就可以使用该语法糖。例如:

          public class MyClass
          {
      
              public MyClass Where<T>(Func<MyClass, T> predicate)
              {
                  return new MyClass { StringProp = "Hello World" };
              }
      
              public MyClass Select<T>(Func<MyClass, T> predicate)
              {
                  return new MyClass ();
              }
      
      
      
              public string StringProp { get; set; }
          }
      

      这显然是一个愚蠢的例子,但请注意,有一个 Where 方法只返回一个新的 MyClass,其中 stringprop 设置为 Hello World。演示:

      MyClass a = new MyClass();
                  var q = from p in a
                          where p.StringProp == "foo" // doesnt matter what we put here, as we're not really checking the predicate
                          select p;
                  Console.WriteLine(q.StringProp);
      

      这将导致写出“Hello World”。同样,这个例子显然毫无意义,但它证明了“where”语法只是在你的代码中寻找一个接受 Func 的 Where 方法。

      【讨论】:

        【解决方案4】:

        查询表达式和扩展方法是做同样事情的两种方法。查询表达式在编译时会转换为扩展方法 - 对于更熟悉 SQL 的人来说,它们只是语法糖。

        当你写这个时:

        var surveyNames = from s in db.Surveys select s.Name;
        

        编译器将其转换为:

        IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);
        

        真的,我认为查询表达式只是出于营销原因而创建的——一种类似 SQL 的语言结构,在开发 LINQ 时充当引人注目的角色,而不是提供很多实际用途的东西。我发现大多数人只是直接使用扩展方法,因为它们会产生更统一的编码风格,而不是 C# 和 SQL 的混合。

        【讨论】:

          【解决方案5】:

          1./ 您的问题标题与您提出的问题不符。
          2./你的问题标题没有真正的意义。 Linq 代表 Language Integrated Query,是一堆技术和实践的总称,IQueryable 是一个常用来促进 Linq 的接口。你在比较苹果和橙子
          3./关于你的实际问题,主要区别在于风格,对于像这样的复杂查询,我个人更喜欢第二个版本,因为它清楚地显示了结果集的进展。

          【讨论】:

            【解决方案6】:

            您的 Sample1 是 Linq 的顶级表示,它更具可读性,并且在编译时会转换为表达式树,即您的 Sample2

            var x = from s in db.Surveys
                join sq in db.Survey_Questions on s.ID equals sq.Survey_ID
                join q in db.Questions on sq.Question_ID equals q.ID
                join qg in db.Question_Groups on q.ID equals qg.Question_ID
                where s.Type_ID.Equals(typeID) & s.Type.Equals(type)
                select new { question = sq.Question, status = sq.Status, grp = qg };
            

            您可以尝试下面的代码来获取书面查询的表达式

            var exp=x.Expression;
            

            查询不太复杂时使用表达式

            【讨论】:

              【解决方案7】:

              我认为您的问题最好这样表述,“IEnumerable 和 IQueryable 在 LINQ 方面有什么区别”

              LINQ 查询默认返回 IQueryable。 IQueryable 允许您在执行查询之前将其他过滤器或“子句”附加到查询中。

              您的 LINQ 查询(第一个示例)和您使用方法链接的 LINQ(第二个示例)产生相同的结果,但语法不同。

              可以将 LINQ 查询编写为 LINQ 方法链,反之亦然。这真的取决于你的喜好。

              @Lucas:不同的是 IEnumerable 执行内存查询,而 IQueryable 执行内存不足。意思是,一旦您在 foreach 迭代器中,您就在使用 IEnumerable,当您通过扩展方法或使用 LINQ from o in object 语法构建查询时,您正在构建一个 IQueryable。 IQueryable 会在您触摸 Enumerator 时立即执行。

              【讨论】:

              • "LINQ 查询默认返回 IQueryable。"您的意思是 LINQ to SQL 或 LINQ to Entities。 LINQ to Objects 使用 IEnumerable。
              • "IQueryable 允许您在执行查询之前将其他过滤器或“子句”附加到查询中。" IEnumerable 也是如此。
              • 哦,问题的标题是固定的:)
              【解决方案8】:

              另一点值得一提的是,Linq 扩展方法遵循 C# 语言,而查询理解的东西是经过预处理的,就像编译器内置的一样。 即您可以导航到 .Select(x => 而你不能为from ... where ... select

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-08-19
                • 2010-12-20
                • 2012-08-11
                • 2016-10-07
                • 1970-01-01
                • 1970-01-01
                • 2012-11-29
                相关资源
                最近更新 更多