【问题标题】:How is transforming this iterator block a functional change?转换此迭代器块如何进行功能更改?
【发布时间】:2014-10-20 08:13:30
【问题描述】:

给定以下代码sn-p:

public class Foo
{
    public IEnumerable<string> Sequence { get; set; }
    public IEnumerable<string> Bar()
    {
        foreach (string s in Sequence)
            yield return s;
    }
}

下面的 sn-p 在语义上是等价的,还是不同的?如果不同,它们的功能有何不同?

public class Foo2
{
    public IEnumerable<string> Sequence { get; set; }
    public IEnumerable<string> Bar2()
    {
        return Sequence;
    }
}

这个问题的灵感来自this questionthis question 针对类似情况提出了不同的问题。

【问题讨论】:

    标签: c# iterator yield-return deferred-execution


    【解决方案1】:

    两者不等价。两个Bar 方法之间如何延迟执行的语义是不同的。当您调用 Bar 时,Foo.Bar 会将 Sequence 评估为 IEnumerable 值。当您枚举Bar2 返回的序列时,Foo2.Bar2 会将Sequence 评估为该变量中的值

    我们可以编写一个足够简单的程序来观察这里的差异。

    //Using iterator block
    var foo = new Foo();
    foo.Sequence = new[] { "Old" };
    var query = foo.Bar();
    foo.Sequence = new[] { "New" };
    Console.WriteLine(string.Join(" ", query));
    
    //Not using iterator block
    var foo2 = new Foo2();
    foo2.Sequence = new[] { "Old" };
    var query2 = foo2.Bar2();
    foo2.Sequence = new[] { "New" };
    Console.WriteLine(string.Join(" ", query2));
    

    打印出来:


    在这种特殊情况下,我们的Bar 方法也没有副作用。如果确实如此,那么理解程序具有的语义以及它应该具有的语义就不会变得更加重要。例如,让我们修改这两个方法,使其具有一些可观察到的副作用:

    public class Foo
    {
        public IEnumerable<string> Sequence { get; set; }
        public IEnumerable<string> IteratorBlock()
        {
            Console.WriteLine("I'm iterating Sequence in an iterator block");
            foreach (string s in Sequence)
                yield return s;
        }
        public IEnumerable<string> NoIteratorBlock()
        {
            Console.WriteLine("I'm iterating Sequence without an iterator block");
            return Sequence;
        }
    }
    

    现在让我们尝试比较这两种方法,看看它们是如何工作的:

    var query = foo.IteratorBlock();
    var query2 = foo.NoIteratorBlock();
    Console.WriteLine("---");
    query.Count();
    query.Count();
    query2.Count();
    query2.Count();
    

    这将打印出来:

    我正在迭代没有迭代器块的序列
    ---
    我正在迭代器块中迭代序列
    我正在迭代器块中迭代序列

    在这里我们可以看到,非迭代器块的副作用发生在方法本身被调用时,而迭代器块的副作用不会在那个时间点发生。然后,稍后,每次我们迭代非迭代器块时,它根本不会引起副作用,但迭代器块每次迭代查询时都会引起副作用

    【讨论】:

    • 恭喜,从last week 开始,您现在是整个网络上one of two 自答逆转徽章的自豪持有者。
    • Jason 不必无礼,看起来他们有一个合理的问题,然后找到了答案。
    • @quantumpotato 他并没有无礼,只是指出他们认为有趣的事情(我也觉得很幽默)。另请注意,我同时发布了问题和答案;在我写下这个问题之前,我就知道答案了。我之所以写它是因为其他人表现出对它的误解(在链接的问题中),所以我发布了这个问题,以防止对该主题的解释(与该问题无关)分散人们对那里实际提出的问题的注意力。
    • 尝试理解投票的逻辑确实很有趣。干得好,回答者,他们似乎在说,因为如此清楚地回答了这个愚蠢的问题。你显然比那个愚蠢的提问者聪明得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-14
    • 2023-03-20
    • 2020-05-18
    • 1970-01-01
    • 2011-03-02
    • 2023-02-08
    相关资源
    最近更新 更多