Как преобразование этого блока итератора является функциональным изменением?

Учитывая следующий фрагмент кода:

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

следующий фрагмент семантически эквивалентен или отличается? Если они разные, как они функционируют по-разному?

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

Этот вопрос навеян этим вопросом, в котором задается другой вопрос о похожей ситуации.


person Servy    schedule 26.08.2014    source источник


Ответы (1)


Эти два не эквивалентны. Семантика отсрочки выполнения между двумя методами Bar различна. Foo.Bar преобразует Sequence в значение IEnumerable, когда вы вызываете Bar. Foo2.Bar2 преобразует Sequence в значение этой переменной , когда вы перечисляете последовательность, возвращаемую Bar2.

Мы можем написать достаточно простую программу, чтобы наблюдать различия здесь.

//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();

Это распечатает:

Я повторяю Sequence без блока итератора
---
Я повторяю Sequence в блоке итератора
Я повторяю Sequence в блоке итератора

Здесь мы видим, что побочные эффекты блока без итератора возникают когда вызывается сам метод, а побочные эффекты блока итератора в этот момент не проявляются. Затем, позже, каждый раз, когда мы повторяем блок без итератора, это вообще не вызывает побочных эффектов, но блок итератора вызывает побочные эффекты каждый раз при повторении запроса.

person Servy    schedule 26.08.2014
comment
Поздравляем, по состоянию на на прошлой неделе вы стали гордым обладателем один из двух значков разворота с автоматическим ответом во всей сети. - person Jason C; 30.05.2017
comment
Не нужно грубить Джейсону, похоже, у них был законный вопрос, а затем они нашли ответ. - person quantumpotato; 13.02.2018
comment
@quantumpotato Он не был груб, просто указал на то, что они сочли забавным (что я также нашел забавным). Также обратите внимание, что я разместил вопрос и ответ одновременно; Я знал ответ еще до того, как написал вопрос. Я написал это только потому, что другие люди продемонстрировали непонимание этого (в связанном вопросе), поэтому я разместил этот вопрос, чтобы объяснение этой темы (которое не относится к этому вопросу) не отвлекало от вопроса, который на самом деле задается там. - person Servy; 13.02.2018
comment
Что насчет этого? stackoverflow.com/help/badges/95/reversal?userid=1749403 - person omikes; 05.03.2018
comment
@oMiKeY Запрос Джейсона не нашел его, потому что он ищет сообщения, которые в настоящее время соответствуют критериям для значка, и это сообщение соответствовало критериям один раз, но больше не соответствует. - person Servy; 05.03.2018
comment
Попытка понять логику голосования действительно забавна. Молодец, ответчик, кажется, говорят они, за то, что так ясно ответил на этот глупый вопрос. Ты явно намного умнее, чем этот глупый задающий вопросы. - person Mark Amery; 04.10.2018