C# 在 Select 语句中调用方法会导致多次方法调用。

huangapple go评论59阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年2月16日 15:56:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75469284.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定