The main differences is that yield return
creates state machine and thus executes lazily. Lazy execution can be problematic in case of validation, let me show some toy example:
// State Machine, Lazy execution
public IEnumerable<int> ReturnClassLazy(int count)
{
// Validation (will be called lazily)
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
var list = Enumerable.Range(1, count);
foreach (var item in list)
yield return item * item;
}
// Eager execution
public IEnumerable<int> ReturnClassEager(int count)
{
// Validation (will be called eagerly)
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
var list = Enumerable.Range(1, count);
return list.Select(item => item * item);
}
In case of eager version (when we return Select(...)
), the method will be executed immediately (note, that validation will be called):
// ArgumentOutOfRangeException is thrown (invalid count argument, -1)
var result = ReturnClassEager(-1);
while in case of lazy call (yield return
version)
// Nothing happens (even if count argument -1 is invalid)
var result = ReturnClassLazy(-1);
... /* imagine 10000 lines of code here */
// Only here (when we materialize)
// the exception is thrown due to invalid count argument -1
int sum = result.Sum();
Usually, we want validation exception be thrown as early as possible, that's why eager version is easier to maintain.