How does transforming this iterator block functional change?

Considering the following piece of code:

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

      

is the following snippet semantically equivalent, or is it different? If things are different, how do they work differently?

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

      

This question is inspired by this question, which asks another question about a similar situation.

-15


source to share


1 answer


The two are not equivalent. The semantics of how execution is deferred between the two methods Bar

is different. Foo.Bar

will evaluate Sequence

to a value IEnumerable

when you call Bar

. Foo2.Bar2

will evaluate Sequence

to the value in this variable when you enumerate the sequence returned Bar2

.

We can write a simple enough program to observe the differences here.

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

      

This prints:

new
old

In this particular case, our method Bar

also has no side effects. If this were done, it wouldn't be noticeably more important to understand the semantics that your program has, and what it should have. For example, let's modify two methods to have some observable side effects:



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;
 }
}

      

Now try comparing these two methods to see how they work:

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

      

This will print:

I am repeating a sequence without an iterator block
---
I am repeating a sequence in an iterator block
I am repeating a sequence in an iterator block

Here we see that the side effects of the nonterator block occur when the method itself is called, and the side effects of the iterator block do not occur at that point in time. Then, later, every time we iterate over the block without an iterator, it does not cause side effects at all, but the iterator block does cause side effects every time the request is repeated.

+22


source







All Articles