LINQ的Func <bool>只被调用一次?

Ily*_*kov 23 c# linq

我迷失了谷歌的关键词...有谁可以请我指向一个MSDN页面或SO答案解释为什么Foo()只被调用一次?特别是因为First只有一个带谓词的重载.这里有什么优化?

using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var foo = "Foo".First(Foo().Contains); // x 1
            var bar = "Bar".First(c => Bar().Contains(c)); // x 3
            var baz = "Baz".First(c => { return Baz().Contains(c); }); // x 3

            Console.ReadLine();
        }

        private static string Foo()
        {
            Console.WriteLine("Foo");
            return "__o";
        }

        private static string Bar()
        {
            Console.WriteLine("Bar");
            return "__r";
        }

        private static string Baz()
        {
            Console.WriteLine("Baz");
            return "__z";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

除了接受和赞成的答案(谢谢),通过ILSpy运行它也有助于在视觉上澄清我的顺序.

private static void Main(string[] args)
{
    char foo = "Foo".First(new Func<char, bool>(Program.Foo().Contains<char>));
    char bar = "Bar".First((char c) => Program.Bar().Contains(c));
    char baz = "Baz".First((char c) => Program.Baz().Contains(c));
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

Fré*_*idi 25

Foo()只调用一次因为你传递给的表达式First()Foo().Contains.

要评估此表达式,Foo()只需调用一次.

让我们考虑第一个和第二个片段之间的差异:

"Foo".First(Foo().Contains);
Run Code Online (Sandbox Code Playgroud)

在这里,First()需要一个Func<char, bool>说法.Foo()被调用(一次)并对Contains结果执行成员访问.该成员访问的结果确实是一个Func<char, bool>,因此代码是有效的,并且该委托被传递给First(),该代理继续为其中的每个字符调用它"Foo".请注意,我们已完成Foo()此处的调用,因为调用委托并不意味着我们必须Foo()再次进行评估.

"Bar".First(c => Bar().Contains(c));
Run Code Online (Sandbox Code Playgroud)

这里,Func<char, bool>传递给的First()是lambda表达式c => Bar().Contains(c).First()将继续为每个角色调用该代表"Bar".lambda表达式的"主体"在每次调用时执行,这导致Bar()被调用三次.

  • 这是现货."First"的参数被评估一次,就像任何其他方法的任何其他参数一样.评估参数意味着调用一次"Foo".你还能说什么呢? (3认同)
  • 或..右..所以它变成了""__ o".包含("F");`..我的错误 (3认同)
  • @BrettCaswell什么? (2认同)
  • @Brett,取决于你所说的"懒惰".`First()`不使用延迟执行,它将立即开始枚举源序列.这是正常的,因为它返回单个元素(或抛出),而不是另一个序列. (2认同)
  • @Rawling,是的..我完全回应了我认为你暗示的事情......这是一个误读..对不起 (2认同)

Chr*_*Fin 16

你需要拆分它直接看到原因:

var foo = "Foo".First(Foo().Contains);
Run Code Online (Sandbox Code Playgroud)

基本上是:

string foo = Foo();                    // only called once
Func<char, bool> func = foo.Contains;  // = "__o".Contains
var foo = "Foo".First(func);
Run Code Online (Sandbox Code Playgroud)

如您所见,Foo仅调用一次并返回"__o".然后Func<char, bool>需要的委托First取自该字符串,这基本上意味着它是Contains字符串"__o"而不是方法Foo,因此"Foo"只打印一次.

在另外两种情况下,你传入一个Lambda表达式,然后为每个字符调用它 - 以与上面相同的方式分割,这将是:

Func<char, bool> func = c => Bar().Contains(c);
var bar = "Bar".First(func);
Run Code Online (Sandbox Code Playgroud)

这里Bar没有被称为构造它Func<char, bool>,因为它只被称为"内部"它的身体,这就是为什么Bar然后在每次调用时调用它Func<char, bool>.