LINQ的Func<bool>只调用一次?
LINQ's Func<bool> is only called once?
我不知道 google 的关键字是什么...谁能给我指一个 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";
}
}
}
编辑:
除了接受和赞成的答案(谢谢),运行 它通过 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();
}
Foo()
只被调用一次,因为你传递给 First()
的表达式是 Foo().Contains
.
要计算此表达式,Foo()
只需调用一次。
让我们考虑一下第一个和第二个片段之间的区别:
"Foo".First(Foo().Contains);
此处,First()
需要一个 Func<char, bool>
参数。 Foo()
被调用(一次)并对结果执行 Contains
的成员访问。该成员访问的结果确实是一个 Func<char, bool>
,因此代码是有效的并且该委托被传递给 First()
,它继续为 "Foo"
中的每个字符调用它。请注意,我们在这里完成了调用 Foo()
,因为调用委托并不意味着我们必须再次计算 Foo()
。
"Bar".First(c => Bar().Contains(c));
这里,传递给First()
的Func<char, bool>
是lambda表达式c => Bar().Contains(c)
。 First()
将继续为 "Bar"
中的每个字符调用该委托。 lambda 表达式的 "body" 在每次调用时执行,这导致 Bar()
被调用三次。
需要拆分直接看原因:
var foo = "Foo".First(Foo().Contains);
基本上是:
string foo = Foo(); // only called once
Func<char, bool> func = foo.Contains; // = "__o".Contains
var foo = "Foo".First(func);
如您现在所见,Foo
仅被调用一次,而 returns “__o”。然后 First
所需的 Func<char, bool>
委托从该字符串中获取,这基本上意味着它是字符串“__o”上的 Contains
而不是方法 Foo
, 因此 "Foo" 只打印一次。
在其他两种情况下,您传入一个 Lambda 表达式,然后为每个字符调用该表达式 - 拆分方式与上述相同,这只是:
Func<char, bool> func = c => Bar().Contains(c);
var bar = "Bar".First(func);
这里没有调用Bar
来构造Func<char, bool>
,因为它只调用了"inside"它的主体,这就是为什么每次调用都会调用Bar
其中 Func<char, bool>
.
在所有这三种情况下,您都传递了一个接受字符和 returns 布尔值的函数。在所有情况下,该函数都将针对字符串中的每个字符执行。不同之处在于这些函数的定义方式。
var foo = "Foo".First(Foo().Contains); // x 1
在这里,您将该函数定义为属于 Foo()
返回的对象的 Contains
函数。要获得 Foo()
只需执行一次。
var bar = "Bar".First(c => Bar().Contains(c)); // x 3
var baz = "Baz".First(c => { return Baz().Contains(c); }); // x 3
在这两种情况下,您定义的函数最终会在函数调用中调用 Bar()
和 Baz()
。
我不知道 google 的关键字是什么...谁能给我指一个 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";
}
}
}
编辑:
除了接受和赞成的答案(谢谢),运行 它通过 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();
}
Foo()
只被调用一次,因为你传递给 First()
的表达式是 Foo().Contains
.
要计算此表达式,Foo()
只需调用一次。
让我们考虑一下第一个和第二个片段之间的区别:
"Foo".First(Foo().Contains);
此处,First()
需要一个 Func<char, bool>
参数。 Foo()
被调用(一次)并对结果执行 Contains
的成员访问。该成员访问的结果确实是一个 Func<char, bool>
,因此代码是有效的并且该委托被传递给 First()
,它继续为 "Foo"
中的每个字符调用它。请注意,我们在这里完成了调用 Foo()
,因为调用委托并不意味着我们必须再次计算 Foo()
。
"Bar".First(c => Bar().Contains(c));
这里,传递给First()
的Func<char, bool>
是lambda表达式c => Bar().Contains(c)
。 First()
将继续为 "Bar"
中的每个字符调用该委托。 lambda 表达式的 "body" 在每次调用时执行,这导致 Bar()
被调用三次。
需要拆分直接看原因:
var foo = "Foo".First(Foo().Contains);
基本上是:
string foo = Foo(); // only called once
Func<char, bool> func = foo.Contains; // = "__o".Contains
var foo = "Foo".First(func);
如您现在所见,Foo
仅被调用一次,而 returns “__o”。然后 First
所需的 Func<char, bool>
委托从该字符串中获取,这基本上意味着它是字符串“__o”上的 Contains
而不是方法 Foo
, 因此 "Foo" 只打印一次。
在其他两种情况下,您传入一个 Lambda 表达式,然后为每个字符调用该表达式 - 拆分方式与上述相同,这只是:
Func<char, bool> func = c => Bar().Contains(c);
var bar = "Bar".First(func);
这里没有调用Bar
来构造Func<char, bool>
,因为它只调用了"inside"它的主体,这就是为什么每次调用都会调用Bar
其中 Func<char, bool>
.
在所有这三种情况下,您都传递了一个接受字符和 returns 布尔值的函数。在所有情况下,该函数都将针对字符串中的每个字符执行。不同之处在于这些函数的定义方式。
var foo = "Foo".First(Foo().Contains); // x 1
在这里,您将该函数定义为属于 Foo()
返回的对象的 Contains
函数。要获得 Foo()
只需执行一次。
var bar = "Bar".First(c => Bar().Contains(c)); // x 3
var baz = "Baz".First(c => { return Baz().Contains(c); }); // x 3
在这两种情况下,您定义的函数最终会在函数调用中调用 Bar()
和 Baz()
。