【问题标题】:What is actually happening with IQueryable.Where()?IQueryable.Where() 实际发生了什么?
【发布时间】:2011-03-31 15:34:10
【问题描述】:

这是根据是否有一些匹配的 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 =>,读作“goes” to"。lambda 的左侧 运算符指定输入 参数(如果有)和右侧 持有表达式或语句 堵塞。 lambda 表达式 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>,它是如何使用“goes to”运算符创建的?

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

【问题讨论】:

  • 如果你真的只需要一个真或假,使用Any函数而不是where+count可能更有效?只要一项为真,db.SaveTrackings.Any(same predicate) 就会返回真。
  • @John 谢谢,如果我能理解这整个委托/lambda 的事情发生了什么,试图让我的头脑了解这些问题
  • 这需要一点时间,然后它就会“点击”。最简单的方法是将它们视为没有名称的迷你函数。语法可能有点奇怪,尤其是没有 args... () => { blah blah blah } 第一次得到所有人。那是什么! :D

标签: c# delegates lambda iqueryable


【解决方案1】:

如果有帮助,请将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 页。我发现这本书很有帮助。

【讨论】:

  • 这无济于事,因为我已经明白这一点,并且有一个“这样想”的例子不是我需要的,我试图了解计算机到底是什么不做导致的行为是什么
【解决方案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 =&gt; someEvaluation 构造创建了一个匿名函数,由以下部分组成:

  • name 只是一个参数的名称,它的类型是从它的用法中推断出来的。您需要一个名称,以便可以引用函数中传递的参数。
  • =&gt; 是匿名函数主体的开始,主体的范围是单个表达式。
  • someEvaluation 是由单个表达式组成的匿名函数的主体。

在我们的例子中,Func&lt;T, bool&gt; 定义了一个函数,该函数接受T 类型的单个参数并返回bool 类型的输出。 (如果我们使用Func&lt;T, U, bool&gt;,我们将采用T 和U 类型的两个输入并返回bool。Func 定义中的最后一个类型参数是返回值。)

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

原则上,您不需要匿名创建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 =&gt; expression 就是语法糖;当它被编译时,C# 将产生一个具有隐藏名称的私有函数并将其转换为上述格式。

【讨论】:

  • 也许这也有帮助:stackoverflow.com/questions/2294053/…
  • 等一下。在您的第一个示例中,您编写了一个函数。这只是为了展示正在发生的事情还是这真的是如何实现的?因为我知道正在发生的事情,所以我只是不知道如何。例如,如果我执行 cout>> 计算机会告诉显卡将文本吐出到我的显示器上。但是我不知道这些事情是如何发生的,我只知道它们有效。
  • @Ryan:是的,上面的函数确实是你将如何实现 Where 函数。不清楚你不明白哪一部分。对于它的价值,大多数 C# 都充满了语法糖,有时会掩盖底层的实现细节。包含yield return whatever 的函数被转换为实现IEnumerable&lt;T&gt; 的类,它本身就是一个状态机,每次调用它的Enumerator 的MoveNext() 函数时都会进入下一步。 (...)
  • (...) foreach 被展开展开成IEnumerator enumerator = enumerable.GetEnumerator(); try { ... } finally { enumerator.Dispose(); }。 parameter =&gt; expr 将匿名函数扩展为私有类,有时也扩展为私有函数,如第二个代码示例所示。而Func&lt;T&gt; f 是Delegate 对象的后代,您在其中调用f(whatever) 会扩展为(T)f.Invoke(whatever)。有很多编译器魔法,并不是所有的都足够有趣。现在,当您说“我只是不明白怎么做”时,您是否使用调试器浏览过代码?
  • 好吧,我的意思不是我将如何实现它,我的意思是微软如何实现它,实际上是否存在这样的代码,或者它只是一个向我展示正在发生的事情的示例?跨度>
【解决方案3】:

Lambda 表达式只是一种缩短代码的方法,但它与声明一个对应于委托类型System.Func&lt;T, TResult&gt;的方法完全相同

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 2013-02-24
    相关资源
    最近更新 更多