I’m writing code that looks similar to this:
public IEnumerable<T> Unfold<T>(this T seed) { while (true) { yield return [next (T)object in custom sequence]; } }
Obviously, this method is never going to return. (The C# compiler silently allows this, while R# gives me the warning ‘Function never returns’.)
Generally speaking, is it bad design to provide an enumerator that returns an infinite number of items, without supplying a way to stop enumerating?
Are there any special considerations for this scenario? Mem? Perf? Other gotchas?
If we always supply an exit condition, which are the options? E.g:
- an object of type T that represents the inclusive or exclusive boundary
- a
Predicate<T> continue(asTakeWhiledoes) - a count (as
Takedoes) - …
Should we rely on users calling Take(...) / TakeWhile(...) after Unfold(...)? (Maybe the preferred option, since it leverages existing Linq knowledge.)
Would you answer this question differently if the code was going to be published in a public API, either as-is (generic) or as a specific implementation of this pattern?
So long as you document very clearly that the method will never finish iterating (the method itself returns very quickly, of course) then I think it’s fine. Indeed, it can make some algorithms much neater. I don’t believe there are any significant memory/perf implications – although if you refer to an ‘expensive’ object within your iterator, that reference will be captured.
There are always ways of abusing APIs: so long as your docs are clear, I think it’s fine.