IQueryable.Where()实际上发生了什么?

时间:2010-08-18 15:50:08

标签: c# delegates lambda iqueryable

这是根据是否有一些匹配的ID返回一个布尔值。

from t in getAll
select new Result
{
...             
    bool DetailsAvailable = 
        (db.SaveTrackings.Where(s => s.BundleID == t.bundleID 
                                  && s.UserID == t.userID)
                         .Count() > 0) ? true : false;
}

这是我认为理解的:.Where()返回所有带有匹配ID的条目,然后.Count()只看到有多少条目。我只觉得我半知道我们需要s为什么。

我知道对这段代码有什么期望,因为它已被使用我只是不明白它是如何工作的,MSDN的一些文档正在使用一些令我困惑的术语。

  

所有lambda表达式都使用lambda   operator =>,读作“去   到了。“lambda的左边   运算符指定输入   参数(如果有的话)和右侧   持有表达或陈述   块。 lambda表达式x => X *   x读作“x转到x乘x。”

那么我想如何理解我的代码基于此的含义,.Where(s“转到”s.BundleID == t.BundleID ...)那么这里发生了什么? “去”是什么意思?是将s中的每个ID与t中的每个ID进行比较?我如何理解为什么它被称为“进入”以及究竟发生了什么?

然后它变得更加混乱......

  

=>运营商也一样   作为赋值(=)的优先级是   右结合。

     

Lambda用于基于方法的LINQ   查询作为标准查询的参数   运算符方法,例如Where。

     

使用基于方法的语法时   在中调用Where方法   可枚举的类(就像在LINQ中那样)   对象和LINQ to XML)参数   是一个委托类型System.Func。 lambda表达式是   最方便的创造方式   委派。

什么是委托类型System.Func<T, TResult>以及如何使用此“转到”运算符创建?

我不能只使用代码,因为我知道它有效,我需要了解如何/为什么。

3 个答案:

答案 0 :(得分:2)

如果有帮助,请将s视为SaveTracking类型的变量。它迭代您的集合/表中的每个s,并测试其BundleID的值。

t是相同的想法 - 就像它正在迭代getAll的所有回复集合。

就像SQL伪代码一样:

 SELECT * FROM SaveTracking INNER JOIN GetAll 
    ON BundleID AND UserID

有关lambda表达式的更深入的技术说明,请查看Jon Skeet's book C# In Depth。第9章,第230页。我发现这本书非常有用。

答案 1 :(得分:2)

也许看看这个函数是手工实现会有所帮助:

using System;
using System.Collections.Generic;

namespace CSharpSandbox
{
    class Program
    {
        static IEnumerable<T> Where<T>(IEnumerable<T> input, Func<T, bool> predicate)
        {
            foreach (T item in input)
            {
                if (predicate(item))
                    yield return item;
            }
        }

        static void Main(string[] args)
        {
            int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            IEnumerable<int> evens = Where(numbers, n => n % 2 == 0);
            foreach (int even in evens)
            {
                Console.WriteLine(even);
            }
        }
    }
}

构造name => someEvaluation创建一个由以下部分组成的匿名函数:

  • name只是参数的名称,其类型是根据其用法推断出来的。您需要一个名称,以便可以参考函数中传递的参数。
  • =>是匿名函数体的开头,正文的范围是单个表达式。
  • someEvaluation是由单个表达式组成的匿名函数的主体。

在我们的例子中,Func<T, bool>定义了一个函数,它接受类型T的单个参数并返回类型为bool的输出。 (如果我们使用了Func<T, U, bool>,我们会选择两个TU类型的输入并返回boolFunc中的最后一个类型参数definition是返回值。)

您可以像调用任何其他函数一样调用Func的实例。如果func接受params,则按预期传递它们,您的参数将绑定到您定义的变量名称。当您调用该函数时,控制流将跳入您的函数并评估其结果。

原则上,您无需匿名创建Func。您可以传入任何具有兼容类型签名的函数,例如:

    static bool IsEven(int n)
    {
        return n % 2 == 0;
    }

    static void Main(string[] args)
    {
        int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        IEnumerable<int> evens = Where(numbers, IsEven);
        foreach (int even in evens)
        {
            Console.WriteLine(even);
        }
    }

该程序产生相同的输出。事实上,在幕后,语法name => expression是语法糖;当它被编译时,C#将生成一个带有隐藏名称的私有函数,并将其转换为上面的格式。

答案 2 :(得分:0)

Lambda表达式只是缩短代码的一种方法,但它与声明与委托类型System.Func<T, TResult>

对应的方法完全相同

我相信C#在编译时会将你的lamba转换为后台方法,它看起来像这样:

bool LambdaExpression(YourType s)
{
   return  s.BundleID == t.bundleID && s.UserID == t.userID;
}