Return All Enumerables with Yield Return at Once; Without Looping Through

Return all enumerables with yield return at once; without looping through

It is something that F# supports with yield! for a whole collection vs yield for a single item. (That can be very useful in terms of tail recursion...)

Unfortunately it's not supported in C#.

However, if you have several methods each returning an IEnumerable<ErrorInfo>, you can use Enumerable.Concat to make your code simpler:

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
return GetMoreErrors(card).Concat(GetOtherErrors())
.Concat(GetValidationErrors())
.Concat(AnyMoreErrors())
.Concat(ICantBelieveHowManyErrorsYouHave());
}

There's one very important difference between the two implementations though: this one will call all of the methods immediately, even though it will only use the returned iterators one at a time. Your existing code will wait until it's looped through everything in GetMoreErrors() before it even asks about the next errors.

Usually this isn't important, but it's worth understanding what's going to happen when.

Yield return results of another Enumerable of the same datatype

You cannot yield return multiple items. If you want to use iterator methods to concatenate sequences, you'll have to loop through them.

Of course, you could always drop the yield return completely and construct your IEnumerable<T> to be returned using other means (LINQ's Concat method immediately comes to mind).

public IEnumerable<string> Validate(ClassToValidate obj)
{
var subObjectMessages = ValidateSubObject(obj.OtherObjectToValidate);

if (string.IsNullOrEmpty(obj.Name))
{
return new[] { "empty name" }.Concat(subObjectMessages);
}

return subObjectMessages;
}

yield return in custom foreach not working as expected

Deferred execution. The code is executed at the time when you consume the yielded items, eg. by looping over the result or by calling .ToList() on it. In your code example it seems like you never consume the result of the iteration, so it is never executed.

Breaking apart code using yield return into different methods

In this specific case, the solution is relatively simple: make Wait() return just the integer:

public IEnumerable Update()
{
while (true)
{
ShootAtPlayer();
yield return Wait(30);
}
}

private int Wait(int x)
{
return removeScript ? -1 : x;
}

In more complicated cases, you can use foreach, though this makes the syntax much more verbose:

public IEnumerable Update()
{
while (true)
{
ShootAtPlayer();

foreach (var x in Wait(30))
yield return x;
}
}

private IEnumerable Wait(int x)
{
yield return removeScript ? -1 : x;
}

As you can see, you can (ab)use yield return to implement fibers, but yield return was never meant for this, so it won't work that well.

What was made for this kind of asynchronous continuations is the new async-await. With that, your code could look for example something like this:

public async Task Update()
{
while (true)
{
ShootAtPlayer();
await Wait(30);
}
}

private async Task Wait(int x)
{
await fiber.Wait(removeScript ? -1 : x);
}

As a final note, I think the way you're using removeScript is not a good idea. End of a script should be signified by the Update() method actually completing (the enumerable doesn't have any more items, or the Task completes), not by returning a magic value.

C# yield return enumerator, continue from part the way through

Look at what the IEnumerable<T> interface actually does.

IEnumerable<T> is completely stateless; it just exposes a GetEnumerator() method, which hands back stateful enumerators that keep track of where they're up to.

You need to pass the same enumerator instance between your two loops. foreach can only operate on IEnumerable<T> (or similar), so you'll need to replace them with while loops that use IEnumerator<T> directly.

Make sure to dispose the IEnumerator in all codepaths (otherwise, finally or using blocks won't run).

Get the first item of an IEnumerable and return the rest as IEnumerable, iterating through only once

Instead of using an out parameter, you can use ValueTuple<T1, T2> (as of C# 7.0, documented here) to return two elements: the first item of the IEnumerable<T>, and the remainder as another IEnumerable<T>.

using System.Linq;

class Program {
static void Main(string[] args) {
(int first, IEnumerable<int> remainder) = GetFirstAndRemainder(Enumerable.Range(1, 5));
// first = 1
// remainder yields (2, 3, 4, 5)
}

// Returns the first item and the remainders as an IEnumerable
static (T, IEnumerable<T>) GetFirstAndRemainder<T>(IEnumerable<T> sequence) {
var enumerator = sequence.GetEnumerator();
enumerator.MoveNext();
return (enumerator.Current, enumerator.AsEnumerable());
}
}

You also need to convert from an IEnumerator to an IEnumerable which I did with an extension method:

static class Extensions {
public static IEnumerable<T> AsEnumerable<T>(this IEnumerator<T> enumerator) {
while (enumerator.MoveNext()) {
yield return enumerator.Current;
}
}
}

Note that due to your requirements, iterating once over the remainder will exhaust it even though it has the type IEnumerable<T>.



Related Topics



Leave a reply



Submit