英文:
C# Having a method call inside Select statement causes multiple method calls
问题
当运行以下代码时,你的主线程会多次调用Convert
方法,如输出所示:
Hello world!
Hello world2!
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
为什么会这样呢?我只在使用yield
和延迟执行时见过这种行为,但在简单的IEnumerable
方法中从未见过 - 我期望的行为是在for
循环中遍历时计算IEnumerable
,然后一次性评估它。显然,当我引入.ToList()
后,问题消失了,但我想在解决问题之前了解这种行为。谢谢提前回答!
【注意】:以上是对你问题的概括和回答,不包括代码部分。
英文:
When running the below code
private static IEnumerable<Tester2> Foo()
{
Console.WriteLine("Hello world!");
for (var i = 0; i < 10; i++)
yield return new Tester2(i);
}
private class Tester2
{
public int Hey { get; set; }
public Tester2(int hey)
{
Hey = hey;
}
}
private class Tester3
{
public int Hey3 { get; set; }
public Tester3(int hey)
{
Hey3 = hey + 5;
}
}
private static IEnumerable<Tester3> Bar(IEnumerable<Tester2> lister)
{
Console.WriteLine("Hello world2!");
return lister.Select(Convert);
}
private static Tester3 Convert(Tester2 tester)
{
Console.WriteLine("Hello world3!");
var hey = "test";
var result= hey.Map(x => new Tester3(tester.Hey)).First();
return result;
}
public static void Main(string[] args)
{
var x = Foo().ToList();
var bar = Bar(x);
foreach (var b in bar)
{
Console.WriteLine("Hmm2");
}
}
I get my main thread calling the Convert
method multiple times, as shown by the output:
Hello world!
Hello world2!
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Hello world3!
Hmm2
Why is this? I've only ever seen behaviour like this when using yield
and deferring execution, but never on simple IEnumerable
methods - I would expect the behaviour to be that it would calculate an 'IEnumerable' when we go over it in the for
loop and then evaluate it once. Obviously when I introduce .ToList()
the issue goes away, but I'd like to understand the behaviour beforehand. Thanks in advance!
答案1
得分: 1
Select
表示"对每个元素应用此函数"。所以对每个元素都调用一次它不应该令人感到意外。由于 Convert
接受单个元素,这是它能够工作的唯一可能方式。
Select
和 LINQ 中的大多数函数都使用延迟执行/惰性评估。这是 LINQ 的主要特点之一。惰性评估与迭代器块(即 yield return 方法)无关。例如,考虑以下情况:
var one = new []{1, 2, 3}.Select(i => i.ToString()).First();
.ToString()
只会被调用一次。这可以非常有用。原始列表的长度无关紧要,因为我们只需要第一个项目。它甚至可以是无限长的!
但是,如果您在常规列表上使用 LINQ,而不是在数据库上使用,某些方法 将 需要在至少需要一个值时评估整个列表。例如 OrderBy
,因为它需要知道所有元素以对它们进行排序。但评估仍将发生在 foreach 循环内部。
英文:
> Why is this?
Select
means "Apply this function to each element". So it should be no surprise it is called one for each element. Since Convert
takes a single element this is the only possible way for it to work.
Select
, and most of the functions in LINQ, uses deferred execution / lazy evaluation. That is one of the main points with LINQ. Lazy evaluation is in no way restricted to iterator blocks (i.e. yield return-methods). Consider for example:
var one = new []{1, 2, 3}.Select(i => i.ToString()).First();
.ToString()
will only be called once. And that can be quite useful. The length of the original list is irrelevant, since we only need the first item. It might even be infinitely long!
However, if you are using LINQ on regular lists, and not a database, some methods will need to evaluate the entire list once at least one value is required. For example OrderBy
, since it needs to know all the elements in order to sort them. But the evaluation would still happen inside the foreach loop.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论