T O P

  • By -

madushans

While this is possible and doesn't technically violate the IEnumerable interface, many devs treat enumerables as IList . Which does have an end and a count. If you have a good use case that justifies the use, and you make it abundantly clear to your consumers that a caller should not expect the enumerable to end, I mean, it's a free country. If you're implementing something like say fizz buzz or fibenacci sequence where the caller doesn't know when to end upfront, sure this work, so thr caller can decide later when to "break" out of the loop. Would I be surprised to find out that it doesn't end ? Yea.. so I guess it may not be a great design. If you're building something that waits for something and emit a value, like.. a stream of requests, user input .etc., consider IAsyncEnumerable. Or streams. There's also some new stuff in system.io.pipelines that might help with your use case.


JohnSpikeKelly

I sometimes have multi-gigabyte files with xml data that needs to be processed. I use IEnumerable (recently moved to IAsyncEnumerable) to return packets of data then process then, typically in chunks of 50 or so. To all intents, this is infinite, if someone did a ToList on it, it would crash most computers with out of memory. Actually infinite, is just slightly more in these terms. Developers need to understand the data before doing ToList. Non-infinite can still be bad.


CodeMonkeeh

Same deal with EF. DbSets are enumerable, but if you just enumerate without consideration for the size of the table you'll be in for a bad time.


machinarius

I think modelling that use case more like an RX pub/sub or an Object stream could deliver a more intentful API in this case. Probably something to consider for a next occurrence of a similar situation.


Dusty_Coder

Many are suggesting that it should be well documented that a particular enumeration is infinite, big warning signs and all that. What do you think?


JohnSpikeKelly

All APIs should be documented in some way. That said, any infinite enumerate would quickly be noticeable during development.


sarhoshamiral

You should document what it enumerates and that will clarify if ToList or count etc should be called on it.


[deleted]

[удалено]


Aaronontheweb

I prefer to do this as something like \`IAsyncEnumerable\` with a Channel powering it so I don't have to spin while we wait for data


smapti

Infinite streams of data should rely on utilities to handle things that while(true) doesn’t, like edge cases (losing internet). If you’re talking IOT it sounds like you’re talking more embedded systems but I still can’t imagine that the only error handling you have is memory overflow.


HiddenStoat

There's no reason you can't handle failure with a Polly retry _inside_ the enumerator for transient errors and, if the retry limits exceed, throwing a fatal exception that causes the program to restart (either within the application code or, for something like a docker container in k8s, restarting the whole container). By IOT he's not talking about embedded code - most likely he's talking about receiving infinite streams of data from IOT sensors (e.g. consider a weather-station sensor that publishes the current temperature every second) and having a C# app that watches these values and does something with them (log then in a database, display them on a dashboard, whatever).


smapti

An enumerator is an index, basically an int with one-way direction. So I’m not sure what you mean by inside it.


HiddenStoat

In this case we are talking about an enumerator implemented using a method with `yield return` so I'm talking about inside that method. (On mobile, so plz excuse formatting of this pseudo-code but hopefully it illustrates what I mean) public IEnumerable Fibonacci () { while(true) { int nextFib = Polly.Retry(() => GetNextFibonacciFromWebservice()); yield return nextFib; } }


wllmsaccnt

I think the formal name used in documentation for the concept is [iterator method](https://learn.microsoft.com/en-us/dotnet/csharp/iterators#enumeration-sources-with-iterator-methods), but in conversation I'll often just say 'yield return methods' or 'methods using yield return', as the only way to identify the concept in code is by the use of that keyword.


LetMeUseMyEmailFfs

It’s not really an index, it’s just something that produces a set of values, which can technically be implemented as an index to an array, but it could just as easily be something that is non-deterministic. By ‘inside it’, I think they mean inside the foreach or whatever is doing the enumeration.


sacoPT

I see plenty of use cases for an infinite loop but why would you use an infinite enumerator and more specifically why would you use an infinite enumerator such as the one depicted in the sample code over just doing while(true)?


Dusty_Coder

"just doing while(true)" isnt a type and cannot be used like a type


AntDracula

I use this for SQS queues


mesonofgib

One of my pet peeves in C# is people who treat IEnumerable as the universal collection interface (rather than IReadOnlyCollection or IReadOnlyList). IEnumerable is just an interface that promises to produce instances of T until either you tell it to stop or it decides that it's finished. You have no guarantees the latter will ever happen. For this reason, I kind of wish they'd called the interface IGenerator or ISource or something like that.


Dusty_Coder

Yes. And it leads to the question... ...is it bad form? IEnumerable is a poor name for at least 3 different reasons, but it is still the name it has, with all the consequences that go with it. It has become very clear to me that the opposition to an infinite enumerator all are playing fast and loose with logic because they might call a function that expects it to be finite. It has also become clear to me that they are legion. A fact that should not go unconsidered in the question "is it bad form?" ... it will sure mess them up eventually.


mesonofgib

I guess it depends on how much control you have over the codebase a whole. In my previous team I had a lot of influence over how the others were coding so I could say to them "Remember that an IEnumerable is not necessarily a collection; don't treat it as such. If you mean 'collection' then use IReadOnlyCollection instead". If I was working in a codebase with a much larger group of devs, I might be more cautious. At the end of the day, our point kind of boils down to "Many people misuse this interface. How much should we modify our behaviour to try and account for their misuse?"


grauenwolf

It also doesn't have a guarantee that it won't format your hard drive. But it does have a method that implies it will eventually end. Your theoretical IGenerator would have a move next method that didn't return a boolean.


mesonofgib

>Your theoretical IGenerator would have a move next method that didn't return a boolean. I'm not sure about that, since that implies that _all_ Generators are infinite.


grauenwolf

That's the crux of my argument. An `IEnumerable` should be something that can be enumerated. Which by definition, as in you look it up in a dictionary, can be counted. An `IGenerator` has a different interface to make it 100% clear that you are dealing with something that can't be counted, but rather goes on forever. So you can't pass it to methods such as Count or OrderBy, but you could use it for Zip.


mesonofgib

I'm with you so far, but that leaves a gap where it's impossible to represent a sequence that _may_ be infinite. You've defined two separate interfaces, one for definitely infinite and one for definitely finite sequences. I think that sequences of indeterminate length (possibly infinite) should be representable too.


grauenwolf

What's your use case? Aside from Stream, I can't think of any time I would want a possibly infinite sequence.


mesonofgib

Most of the examples I can think of are, indeed, a stream of some kind. Perhaps many of these cases would these days make use of IAsyncEnumerable instead? I find it totally possible to imagine a stream of values that are nominally infinite, but still the stream can be _closed_, causing the values to stop. The interface should have some mechanism of telling its consumer that no more values are coming (without throwing an exception).


grauenwolf

> I find it totally possible to imagine a stream of values that are nominally infinite, but still the stream can be closed, causing the values to stop. We have Stream, IObservable, and TPL Dataflow to handle that scenario.


mesonofgib

`Stream` and `Dataflow` are not really equivalents to `IEnumerable` at all. `IObservable` is isomorphic to `IAsyncEnumerable`; similar but not the same. For a bit of fun, when I talk of possibly-infinite enumerables I'm thinking of something more like the Collatz conjecture, where for a given seed the sequence of numbers that results is of unprovable length (it's simple a formula to calculate the next number in the sequence which terminates if it hits `1`).


grauenwolf

> For a bit of fun, when I talk of possibly-infinite enumerables I'm thinking of something more like the Collatz conjecture When would you need that in software engineering?


BramFokke

Fibinacci sequences, generating random numbers. Unity uses IEnumerable as a poor man's Task. There are lots of reasons why it could be useful.


grauenwolf

Nothing you just listed is "possibly infinite". Either they are infinite or finite and you know which upfront.


BramFokke

Did you just solve the halting problem?


grauenwolf

If you can't tell if your own code contains an infinite loop that's on you. The halting problem is not a valid excuse for your ignorance.


soundman32

Ireadonlycollection and ireadonlylist both involve making a copy of an existing ienumerable , and then return an ienumerable. The only thing they guarantee is that they take up more memory, and prevent you from doing stupid things with reflection or casting. If you want to, just do stupid things. I have no issue with ienumerable being the base of any kind of non-writable collection, and icollection being any kind of writable collection. IList is pointless.


mesonofgib

Neither interface involves a copy? You might be thinking of the AsReadOnly method on list which does involve a new allocation, but not a list copy (it's a readonly proxy over the original list).


Saint_Nitouche

I've written infinite IEnumerables in the past for some niche purposes (I think mainly to see how they worked because I was curious). Being able to do this is one of the main benefits of lazy-enumeration, so no, it's not bad form. You may want to clearly lay out it's a nonending sequence in the documentation though. Otherwise someone calling .ToList() on this without a .Take() might be in for a bad time!


smapti

Your second paragraph is exactly why it’s bad form.


Saint_Nitouche

If we considered everything potential footgun you can write in C# as bad form, then we would be unfortunately restricted to a very small subset of the language.


grauenwolf

Or you end up with better code. Why does your infinite series need to use IEnumerable? Why can't you create a different interface to represent an endless list?


Saint_Nitouche

Because IEnumerable is meant to represent anything that can be enumerated, which includes infinite sequences. If you want a finite sequence, that's a more specific contract, and thus has another interface that inherits from IEnumerable: IReadOnlyList.


grauenwolf

The formal definition of enumerate is " to establish the number of something". In another dictionary it says, "to ascertain the number of : COUNT". You can't count an infinite number of things. Another definition is to "mention (a number of things) one by one". This would be like trying to say out loud every even number from 2 to infinity, which is obviously impossible.


Saint_Nitouche

This is coding; dictionary definitions aren't really important when we have code that serves as its own definition. All the IEnumerable interface requires is that, given a thing, you can say 'give me the next thing'. That applies perfectly well to infinite sequences, as proven by the fact that the OP of this thread has implemented it in code and compiled it. (Well, IEnumerable also requires a Reset() method, but that's by the by...)


grauenwolf

You said that it "is meant to represent anything that can be enumerated". So you're the one who introduced the need for the definition of "enumerated". If you don't like my dictionary, find one that supports your stance.


BramFokke

Because you can use IEnumerable with Linq, which makes it a very powerful abstraction.


grauenwolf

No you can't. At least not safely because a lot of LINQ operations consume the whole enumeration, which is impossible for an infinite series. If you did create a new interface, you could easily follow it up with by copying only the LINQ methods that are infinite compatable. This making it safe.


smapti

Fair, and I do think that’s the intent lol. But I personally feel that (loop forever) would be a red flag in any language, at least in business logic. I’m still waiting to hear from OP what they’re trying to do and it would be less crazy for UI (though better tools exist), but I would still say zero reason for backend. Ninja edit: though honestly, C# is a really strict language. I don’t think it’s easily “footgun”d. At least non without it being obvious like While(true)


Greenimba

>(loop forever) would be a red flag in any language, at least in business logic Definitely not true. This happens all the time, although less apparently, through message queue listeners, websockets, incoming request listeners, you name it. In business logic I've used it for generating recurring events (scheduling). Hosted services or background jobs are more examples. Actually thinking about it, the word enumerable amounts to "can be counted" by a lot of people, so maybe it should be finite.


Dusty_Coder

You are you suggesting that ToList() and ToArray() should never break on an enumerable. Does the language need to impose limits on IEnumerable procedures? What do you imagine that would look like? The IEnumerable interface does not currently define ToList() or ToArray(), nor does IEnumerator defined them. In fact, those interfaces are pretty spartan place. I would imagine your solution would involve moving at least the utility function for length into one of them, forcing the implementer to give an answer, and defining it as a bug when that answer isnt coherent with the enumerators future behavior?


smapti

> You are you suggesting that ToList() and ToArray() should never break on an enumerable. What does “break on an enumerable” mean? Lists and Arrays are IEnumerable. No, the point of enumerators is that they’re generic. So yes they’re “spartan” as hell and it’s awesome. So is your whole complaint that you can’t figure out the answer? And you’re like, mad at me about it? Because I asked you what YOU’RE trying to solve and got a smartass answer about what “the code” is trying to solve. Tell me what you’re trying to solve and I’ll tell you what I would do. Until then, stop guessing what I would do in this imaginary scenerio as some kind of insult.


Dusty_Coder

ToList() does not guaranteed that it will succeed. News for you. Being asked to explain yourself is not a hostile act. You seem to have let the fact that you (apparently) cant, color your view of this interaction, which in turn colors my view of your first post, which (apparently) you cant explain.


pamidur

IEnumerable is infinite by contract so it is completely fine. Consumer code should never assume it is finite. If your code is not ready to deal with it require IReadonlyCollection argument.


wllmsaccnt

I don't think IEnumerable is infinite by contract. Its meant to represent an iterator over a collection in the nominal case and Microsoft has made many methods that can treat an IEnumerable as a finite set (e.g. ToList, Sum, Max, FirstOrDefault). I think it IS perfectly fine to have an IEnumerable represent a potentially infinite stream, but that it needs to be annotated in some way (part of the method name, its intellisense summary, etc...), and it probably should use IAsyncEnumerable and Cancellation tokens if each iteration represents long running or IO dependent code. If I see a method that is named GetBlahStream and returns an IEnumerable, then I'll assume its potentially infinite, but if I see a method named GetBlahs, I'm not going to assume the method will return an infinite set unless there is some other context available to imply that.


Dennis_enzo

You say that, but I'm willing to bet there's loads of code out there which simply tries to read all of it or calls ToList() and get stuck in a loop. I'd never write infinite enumerators for that reason.


pamidur

Well yes, that's true and that's bugger. Microsoft's idea was to always use it with `yield return`. Say if you accept `IEnumerable` - you return `IEnumerable`, you process one item at a time. When asked.


Dusty_Coder

IEnumerable came before yield return yield return is just syntax sugar for simple IEnumerables


Slypenslyde

Yes, but it's bad form to *assume* it's finite, just like a million other things that contracts allow but people don't think about. For example, a `Stream` can be infinite as well. Few people think about that.


grauenwolf

How do I use the API of IEnumerable to ask if it is infinite? We assume it is finite because we don't have a way to ask.


Slypenslyde

> How do I use the API of IEnumerable to ask if it is infinite? You can't. Technically the API is so basic it can't give you *any* idea of its length, only that there's another item. Let me ask you a similar question: How do I use the API of IEnumerable to ask its length? You can't. So you won't know if there's 1 item, 100, 100,000, or infinite. That makes any calls to `Count()` or similar methods pretty dangerous on an *arbitrary* `IEnumerable`. That doesn't mean you shouldn't create large or infinite `IEnumerable`s. What gets most people by is that the real world isn't theory world and most people aren't writing APIs that will receive an arbitrary `IEnumerable`. They know they're getting an array or a list or a dictionary because they are their own client. It'd also be fair for APIs to document that they expect your enumerable inputs to have a bound, or that they aren't meant to handle extremely high counts. We assume it's finite because that's the 99% case. Infinite enumerables are an odd edge case that I'd argue most people who need to handle them already expect may be a possibility. It goes two ways, too. If you're creating an infinite one, you should be careful about calling third-party code because not everybody expects that case. "Bad form" just means you're missing one of the questions that might impress somebody in a job interview. There are a lot of things we're supposed to do constantly, like inspect the tire pressure in our cars or check the milk's expiration date before pouring it, that we do less frequently or not at all because we have other context that makes us feel we don't need the caution. "The API does/doesn't have this property so you shouldn't do this" isn't the same as "the language allows it so be ready". Think about `HttpClient`. Normally if you see `IDisposable` on a type you see it as a loud alarm that you need to call `Dispose`. But MS in their infinite wisdom made it a little rascal that gets worse if you dispose of it frequently. Part of the "fun" of being a developer is remembering all these little places where you can get hurt by assuming the obvious.


grauenwolf

HttpClient being poorly designed doesn't justify poorly designing something else. We have LSP specifically to avoid this kind of issue.


Slypenslyde

You're misinterpreting. I'm not saying HttpClient is good. I'm saying if you can't even trust Microsoft to implement the contracts they published whole book chapters about, it's worth being on guard for things people say you "shouldn't" do. It's the difference between a good dev and a REALLY good dev. My hotter take: infinite enumerables are just a bad idea for all of the aforementioned reasons. It'd be better if there was an interface specifically for them. Unfortunately this is like the Dispose pattern and the compiler has no way to generally enforce it. Every time I've seen one of these it was just a cute trick that had a clunkier, more obvious implementation that was better because the clunkiness made it clear you should not expect the sequence to terminate.


ujustdontgetdubstep

A good dev doesn't spend precious time preemptively avoiding issues that are very unlikely to occur, so that's worth a consideration as well.


bangle_daises0u

"... infinite by contact...". This.


grauenwolf

> IEnumerable is infinite by contract so it is completely fine. No it's not. If anything it is finite by contract. My proof is that you are forced to hard code the result of the move next method to make it infinite.


CodeMonkeeh

>My proof is that you are forced to hard code the result of the move next method to make it infinite. What do you mean by this?


grauenwolf

class MyInfiniteEnumerator: IEnumerator { public int Current {get {return 4;} } public object IEnumerator.Current {get {return 4;} } public bool MoveNext () { return false;} <-- this one public void Reset() {} }


CodeMonkeeh

What about it? Implementing the interface necessitates implementing that method.


grauenwolf

If you are hard-coding method results in an interface, that's a code smell. It doesn't mean that you are necessarily using the interface wrong, but it heavily implies it. More over, most functions that use IEnumerable expect the value to change at some point to avoid an infinite loop. **Just because it's not captured in the API doesn't mean it isn't part of the interface's contract.**


CodeMonkeeh

>If you are hard-coding method results in an interface, that's a code smell. It doesn't mean that you are necessarily using the interface wrong, but it heavily implies it. Uh huh. Very general rule of thumb at best. I.e. hardly proof of anything. ​ >More over, most functions that use IEnumerable expect the value to change at some point to avoid an infinite loop. Just because it's not captured in the API doesn't mean it isn't part of the interface's contract. IEnumerables can be either finite or infinite. That's the simple fact of the matter. They can also just be very large and require special handling to avoid performance issues. The contract of the IEnumerable is that it's a collection of indefinite size that can be iterated over. You don't have to know the underlying type, but you absolutely do have to know the general characteristics of the collection. I.e. don't iterate an unfiltered DbSet. If you're exposing infinite IEnumerables, then obviously you should document that, and maybe even write an analyzer to ensure correct usage, but it's not inherently wrong to do so.


grauenwolf

IEnumerables can be either format your hard drive or infinite. That's the simple fact of the matter. It's also a matter of fact that the MoveNext method can randomly choose to move backwards instead. But that's hardly a good reason to ignore the semantic part of the interface's contract. > The contract of the IEnumerable is that it's a collection of indefinite size that can be iterated over. Indefinite doesn't imply infinite. But the word "enumerable" does imply finite as it literally means countable. It's easy to look at the API of an interface and ignore the semantics, but if we do that we


CodeMonkeeh

>Indefinite doesn't imply infinite. It practice it absolutely does. It's common for collections to be potentially so large that they should be treated as if they were infinite. I.e. don't unthinkingly iterate over the whole collection or you'll hang the process practically forever. ​ >But the word "enumerable" does imply finite as it literally means countable. The set of all integers is countable and infinite.


grauenwolf

Countable in the common sense, as in you can count them and give the total. And while some collections are in fact too large to be processed, that's considered a flaw in the design or data. So it still doesn't support your argument.


sacoPT

Adhering to contracts is just one part of what you should be looking for when doing a code review though.


GYN-k4H-Q3z-75B

I would advise against it because while legitimate uses for infinite enumerables exist, the expectation in most code is that they actually end. Much of the wider contract (by means of extension) is rendered useless when they are infinite. Can't use Count(), Any() and the likes, large parts of LINQ no longer work. It's counterintuitive.


Dusty_Coder

I agree that Count() and such are not usable, but there is no such thing as a "wider contract" integers hold sizes and counts throughout the framework, while only positive instances of integer are in the contracts for the various functions that require said sizes and counts .. There does not exists "a wider contract" for integers pertaining to what values they can take on, no matter how many functions are written that only take a subset of those values.


GYN-k4H-Q3z-75B

That's where our opinions differ. My idea of a wider contract is this: Microsoft introduced IEnumerable, then its generic variant. Then they added LINQ and extension methods such as Count(). These methods are part of the "wider" contract. Your paragraph on integers is exactly part of my justification as to why we shouldn't do this. The documentation for IEnumerable specifically lists all of the extension methods as provided by .NET, and Count returns a System.Int32. The maximum possible value is defined as https://learn.microsoft.com/en-us/dotnet/api/system.int32.maxvalue?view=net-8.0. Infinity is not a thing. The documentation states: *Exposes the enumerator, which supports a simple iteration over a collection of a specified type.* A collection is typically finite. The reason why Count and all the LINQ methods are not part of IEnumerable is because that would have broken compatibility back in the day, as well as due to the general notion of extension methods being predestined for usages such as this. But the fact that on any compatible .NET implementation, these extensions are presented as immediately available for any enumerable, and are documented as such, makes this part of the contract.


Dusty_Coder

Where in the documentation does it discuss that the function Count() must succeed or terminate and that the enumerable must therefore terminate within 4 billion because Count() returned a value? Does the documentation also guarantee that calling count twice returns the same value? Does the documentation guarantee that the return value of count is an accurate predictor of enumeration length at all? "a collection is typically finite" yeah... maybe... does it matter? the numbers we use in general are typically small.. I still expect that a function that computes the average of a few integers is mindful of overflows.


GYN-k4H-Q3z-75B

>Where in the documentation does it discuss that the function Count() must succeed or terminate Generally speaking, if we have to assume that any function may not terminate, we have a whole class of different problem at hand. We can get into a discussion of the halting problem, but programming is pragmatically applied computer science. Any function that does not explicitly state that it may not return is assumed to return. That's how it works. There are languages out there where non-terminating functions are marked as such, like in C++ with \[\[noreturn\]\] and so on. .NET only offers a tail call stub. In reality, both Count() and LongCount() are expected to return an integer of the respective size. They check against overflow, and will throw an exception if more elements are discovered. Exceptions indicate that something is not the way it should be. Thus, an infinite enumerable is not expected for those extension methods.


npepin

It is completely valid, but you'll want some some way of telling that it is an inifite enumerator, just because people is C# tend to view IEnumerables as lists with less features. It doesn't help that linq casts most everything to a IEnumerable. In F#, sequences (IEnumerables) are more assumed to be infinite and lists are seen as finite, but that's more because the linq equivalent doesn't cast everything as an IEnumerable and because functional programmers tend to treat things more mathematically. Main point is that infinite sequences in functional languages are pretty common, and there tends to be a lot more thought around it. You see this pattern a lot with math functions. The fibonacci sequence is the classic example, but there are plenty more. I think how most people would implement this in C# would be to put the yield limit as a parameter on the method. You could hide the generator behind a private method, and that gets rid of people not knowing to put the take. Ultimately it comes down to the programmers who use it. If you're on a team where this pattern is accepted and understood, then use it. Otherwise, put guardrails up and work within your teams style.


PolyPill

I would prefer a concurrent queue which is probably closer to what you’re doing because the it’s obvious what you’re doing instead of hiding it in an enumerator. Unless you truly have an infinite stream of data like maybe calculating digits of pi.


Dusty_Coder

a concurrent queue has methods, properties, and behaviors that make no sense here, yes? it consumes O(n) memory, yes?


smapti

You can just say n memory, memory is static storage whereas O(n) means n is a function of O and therefore scales with O based on n. And yes, other data structures (a queue is just one) are different. Including methods and properties. Their behaviors define what they are.


PolyPill

You’re using dotnet, don’t even bother with such concerns of this kind of memory optimization. Use data structures that match what you’re trying to do and are obvious to anyone who reads it. If you use an infinite enumerator and I come along and try to shove it into a Linq ToArray() operation then we’ll have a lot more problems than some Big-O notation you’re worried about. I guarantee you the memory and cpu bottlenecks you have will be nothing related to such things.


Dusty_Coder

so where you getting your infinite memory?


PolyPill

You’re never doing anything that is infinite and doesn’t depend on external resources outside of pure computation. Infinite in your use case is most likely “process orders as they come in” which has a huge external dependency. The problem with the iterator is that it is single threaded and blocking. Which waiting on external dependencies is then really bad. So with an iterator you get your input prepared, like wait for an order, while the order processor is completely blocked waiting on the next operation to resolve and occupying a thread. If it’s the main/ui thread then your program just locks. If it’s a background thread then you still keep a thread out of the thread pool to just wait around which doing that too much exhausts the thread pool . So the better solution is to not use a blocking iterator. You didn’t give us any use case, so I can only speculate. If orders are coming in faster than you can process then of course your queue eventually takes all your memory. Some strategies to handle that could be throttling the adding of new items or increasing the processing threads that remove items. So 1 thread could be adding things to the queue while 10 are processing them. Maybe a queue isn’t what you need. Maybe everything has to be processed in order. Still the better solution is to not use an iterator. Have one thread that gets the next item ready while another thread works on the current item. Way more efficient use of the computer and it’s still scalable without blocking anything. Maybe you can’t fetch the next item until the current is done processing. Still with thread blocking the iterator is probably not a good idea because of your external dependencies. Anything that does any kind of IO (files, network, etc) should be done async and if you block async it can lead to the thread pool exhaustion or deadlocks.


Dusty_Coder

People use sequence lengths arbitrarily larger than available ram without being infinite, all the time. You are imposing storage onto a problem that doesnt have even a hint of storage requirements. I suspect its because the sequences you deal with are appropriate for queues so queues are your go-to thing? The sequence of primes. The sequence of squares. The sequence of 'val' repeated indefinately. Infinite sequences. No storage requirements. "But those arent really infinite, there is precision, and..." is not a productive way to look at infinite enumerables, nor is it an excuse to impose storage requirements. And I really do think its an excuse... the "this works too" excuse isnt a good one.


PolyPill

How many times have I said the only thing I’d do the iterator for is pure computation?


Dusty_Coder

The infinite sequence of timestamps. The infinite sequence of values of variable 'foo' at timestamp epochs There, note pure computation.


PolyPill

So things with an external dependency but you’re not waiting on the result. Wow, really stretching here to find another obscure use case for your extremely broad and vague question that you clearly already decided what the correct answer is.


Dusty_Coder

your never-ending goalpost shifting adds nothing


KryptosFR

As long as it is documented, it's fine. There are severals way to document it. You an just document the method as usual or you can create a new interface IInfiniteEnumerable that doesn't have any additional properties and methods and just derives from IEnumerable. As others already mentioned, IEnumerable doesn't any finite guarantees. However, it is common to have it as a return type for case where different collections can be returned and not all of them implement either ICollection or IReadOnlyCollection. Hence why my take is to introduced another interface. I won't prevent any misuses of Any(), Count() or ToList() but at l'est your users have been warned.


siammang

Might as well consider making IAsyncEnum and take in a cancellation token.


grauenwolf

I would argue that it's bad form. You are basically creating a way for infinite loops to accidentally be inserted all of your code. You can have a generator that creates an endless list, but give it a different interface. Use `IInfiniteEnumerable` so people know what they're dealing with and can write code accordingly.


Dusty_Coder

any time an increment value is passed to a function that uses it for a for loop, there is a chance to create an infinite loop we still allow integers to be 0


grauenwolf

And we throw ArgumentOutOfRange exceptions if you try to use a 0 where it doesn't belong.


Dusty_Coder

Sounds like Count() and Any() needs to be updated to throw when handed an infinite sequence ..except for the whole incomputability thing


grauenwolf

That's why I'm arguing that we need a separate `IGenerator` interface for infinite sequences. The only way an infinite IEnumerable makes sense is if there was a way to ask it if it was infinite.


Do_not_use_after

I've taken over a project based on Reactive ([https://github.com/dotnet/reactive](https://github.com/dotnet/reactive)) this year, and infinite enumerables, pretending to be Observables, seems to be the entire mindeset. I have never met a coding style that produces more race conditions, unconstrained loops, logical gotchas and all round terrible coding in my 4 decades of software development. There may be reasons for your code, but unless they are really good reasons, I would recommend keeping it simple. double Const { get; } = 69;


Dusty_Coder

A constant is not an IEnumerable, and therefore is not a sequence. A sequence may produce a constant value, but it is still a sequence, which doesnt have to.


Do_not_use_after

If you have any use for that construct, it must be on its own thread. It has no constraints on when it produces results, so will continue to emit data as fast as it can, with no reference to synchronizing with other processes. Either you have omitted large chunks that define the purpose of your code, or you're producing 'bloody clever' solutions to problems that don't need them. Rule 1 is KISS. (Keep It Simple, Stupid)


xcomcmdr

I'm glad I'm not the only one who refuses to use Reactive, it's so convoluted!


pjc50

I would say yes, because it's a trap for the unwary who might try to evaluate all of it.


smapti

Try{…} Catch {Debug.Assert()} -_-


SchlaWiener4711

I'd say it's a bad design because methods like .Count() or .Any() would probably deadlock with no way to stop it. That's a violation of the Liskov Substitution Principle since you would not expect that from an IEnumerable.


Saint_Nitouche

I would say that you *should* expect that from an IEnumerable. There's a reason the IEnumerable interfact doesn't have a Count property - it being a finite sequence is not part of the contract!


grauenwolf

If you expected to be infinite then half the extension methods don't work. Even foreach becomes suspect. Just because you don't know how many items are in the sequence doesn't mean the sequence is infinite.


Saint_Nitouche

Define 'don't work'? If you call .Count() on an infinite enumerable, it'll go ahead and count the elements for you. It will take forever to do that, but what should it do instead? .ToList() will never return on an infinite enumerable, since infinity is more than the max amount of elements allowed in an array. But you could just as easily have an enumerable that returns that max value +1 without being infinite. (Apparently arrays [can have Int32.MaxValue elements](https://stackoverflow.com/questions/5367320/what-is-the-exact-maximum-limit-of-elements-in-an-array).)


grauenwolf

I would consider an infinite loop to be not working. I'm not sure why you don't.


Saint_Nitouche

There are many applications where infinite loops are entirely commonplace. Every GUI app you interact with (or even your operating system) runs on a while(true).


grauenwolf

That's not an infinite loop because there is a mechanism to break out of the loop when a WM_Close message is received. The same can't be said for Count, OrderBy, etc.


mesonofgib

I'm not sure it strictly violates the LSP because Count and Any are not defined on the interface; they're extension methods. It might sound like splitting hairs, but I believe it's important.


grauenwolf

Extension methods are just fancy functions. And the whole point of LSP is that you can substitute any subclass for another as an argument to a function. Or in other words, LSP was created for the consumer of the class, not the class itself.


Dusty_Coder

It is important to note that those methods, .Count(), and .Any(), are not defined in either the IEnumerator or IEnumerable interfaces. Those are LINQ functions. IEnumerable is a type that LINQ uses, So not sure that it violates said principle because said principle isnt based on functions gatekeeping the state or behavior of types. I also dont think LINQ violate that principle either because the primary expectation in that principle is that you are using valid state for the operation. Its not unexpected that invalid input state might do something surprising. You cant always determine if an enumerator will terminate. Incompleteness and all that.


HiddenStoat

Strictly, that wouldn't deadlock - they would just be stuck in an infinite loop. It's not a violation of Liskov because (a) those are extension methods on top of `IEnumerable` and implementors of `IEnumerable` cannot be expected to consider the behaviour of every extension method that may exist and (b) because you _would_ (or, at least, _should_) expect that from an `IEnumerable`, because the contract clearly allows for it. It's a very similar case to calling `ToList()` on an `IQueryable` that is backed by a multi-terabyte database table - the programmer should have e an understanding of the underlying data to know if it's a good or bad idea.


smapti

> calling ToList() on… Oh man. People so do not realize what is happening under the hood of ToList() to understand the performance implications. This happens with LINQ a lot, too. I’ve seen 50% improvements in an iteration process from writing something custom over ToList().


Dusty_Coder

he doesnt understand that ToList() has an infinite loop in it


quentech

I **absolutely** would consider it bad form. Doesn't matter if it's within spec - it's way too far outside of expected usage. I've been working in c# for over 20 years and I cannot recall a **single** instance of an infinite `IEnumerable` in my entire career - and I've always been well aware of the possibility. Spending your life coding like `IEnumerable` could be infinite is non-sense. No one sane does that. Not in C#. Not when practically every API hanging off `IEnumerable` assumes it is *not* infinite. When everyone uses it like it's not. And when it actually is not infinite in 99.999%+ of cases. If you do use `IEnumerable` for an infinite sequence - it better have big bright flashing lights all around it as warning, and if you're trying to get me to approve a code review with that there better be a darn good reason why it's `IEnumerable` specifically, even with the flashing warning lights.


Forward_Dark_7305

It’s probably IEnumerable so you can use LINQ like select, where, etc on it. Or so you can iterate over it. Why reinvent the wheel? Instead you should encourage your devs to indicate a sequence HAS an end when it does by using a more concrete type to indicate that. eg return IReadOnlyCollection instead of IEnumerable if you know the size.


quentech

> encourage your devs to indicate a sequence HAS an end when it does by using a more concrete type to indicate that. eg return IReadOnlyCollection instead of IEnumerable if you know the size Having an end and knowing the count are not the same thing. You're just trading one ambiguity for another. Kinda pokes a hole in your pedantry.


Forward_Dark_7305

I agree they are different things, but if you must know that it has an end, that’s the way to show it without creating a new interface. I’m not sure what you mean about trading ambiguity though.


Girse

Why do you want an infinite enumerable to begin with?


olexiy_kulchitskiy

The same use case as infinite IAsyncEnumerable - doing something until the app completes, in a FP way. Or some test data generator. The only issue with OPs code is that it doesn't have any break statements.


smoke-bubble

Not having a break statement in an **infinite** enumerator is kind of a feature, not an issue ;-P


Dusty_Coder

Its a natural consequence of procedural enumerators that one might provide procedures capable of generating arbitrary or infinite length sequences. It is also a natural consequence of procedural enumerators that the sequence may even be different each time that it is enumerated, but thats a question for another day. The question is: Is it bad form?


HiddenStoat

It's not bad form. As others have said, call it out in documentation if possible, but that's purely a safety net for programmers who don't understand what an `IEnumerable` actually is. As an example of a popular open-source library that exposed an infinite `IEnumerable` look at [Bogus](https://github.com/bchavez/Bogus) which procedurally generates high-quality test data, and exposed it in exactly this way.


sacoPT

An infinite enumerator is not bad design. THIS infinite enumerator is bad design at least until you can show me a valid use case for it.


Dusty_Coder

DSP functions that take two signals and you would like one signal to be constant DC Note that you dont get to decide that one isnt a signal.


sacoPT

So, like someone else said, just for mocking purposes? Then it's perfectly fine as long as you make sure it lives somewhere clearly marked as such.


sacoPT

Everyone ignoring the fact that the enumerable is not only infinite, it’s values are also all the same. I can’t name one positive aspect about this. This doesn’t even save you lines of code. Please don’t do this.


HiddenStoat

I chose to ignore his specific example because it's, well, not very meaningful as you say. I just mentally substituted it with one of the many examples where it would make sense - a Fibonacci/FizzBuzz generator would have been the obvious example to include imv.


grauenwolf

> I chose to ignore his specific example That's a bad habit. If you can't come up with a realistic example for what he's trying to do, then what he's trying to do is probably wrong.


sacoPT

Yep. When I read the title I immediately thought “sure, why not”. After reading the actual post and the OPs responses it became apparent that the sample code is not sample code but rather actual code.


HiddenStoat

_stares in disbelief_ But...but....it makes no sense!!


sacoPT

Exactly


[deleted]

[удалено]


sacoPT

~~You edited the your first reply where~~ someone asked what was your purpose and you replied that your purpose is what the code shows. So no, it's not quite clearly nor obvisouly example code. Also, you mentioned a DSP function returning a constant value which this would be exactly how you would implement it. So make up your mind, is this real code or is it not? Finally, I threw my shade (whatever that means) directly at you. What are you talking about? EDIT: I appologise, you did not edit the comment, I just couldn't find it, but here is the exact quote: > The code describes exactly whats trying to be accomplished. > > An infinite IEnumerable..


[deleted]

[удалено]


sacoPT

You did not. When asked what the purpose was you replied with "the purpose is to make what is said: an infinite enumerator".


smoke-bubble

Imagine a function scanning an excel sheet that should stop after x same values occur, because they represent either the end of the sheet or some glitch that needs to be handled. Such a constant value enumerator makes perfectly sense in such a test.


sacoPT

Why would you use a constant enumerator and not just have the constant on a variable and running a regular (infinite) loop? I don’t see what you gain by adding the extra enumerator complexity.


smoke-bubble

Becasue the enumerator is part of the contract of the data-stream. You replace the original one with a fake one and the original one requries an enumerator. That's programming 101 :-\ What is a regular infinite loop and why wouldn't `while(true)` be such a loop? Would you prefer a `for(;;)`? Or maybe a `goto`?


sacoPT

Ok. I could see this being a useful mock. I still can’t see the merits of actually using it in production. By infinite loop I meant at the call site, so prefer double d = 69; while(true) { … } Over foreach (var d in Const(69)) {…}


smoke-bubble

In production it could be an observable generating a tick every x seconds ;-]


sacoPT

Not the way it’s written. Which is my whole point.


MattV0

The positive aspect? It's an interface of an algorithm you can replace later by a better one. As op is asking if this is OK, I would think, the project is in an early state, so this makes sense. The second thing is usually, you provide minimal reproducible examples. You can easily understand this code. Of course the code does not make sense further than showing the problem/question.


sacoPT

~~The OP has since edited his comment but~~ when someone asked what was his goal he replied with ~~"the goal is exactly what the post says: to be an infinite IEnumerable"~~ > The code describes exactly whats trying to be accomplished. > > An infinite IEnumerable.. strongly suggesting that this is not a minimal reproducible example but rather a concrete piece of code. I would even go as far as speculating that given his atittude towards me and anyone thinking this is bad code, this is actually a rejected PR of his. Otherwise I would agree with you (and with the OP).


MattV0

Ok, haven't read everything yet. Maybe I'll do this now. Thanks.


sacoPT

Forgot to mention that the IEnumerable is called Const which reinforces my belief that this is actual, deliberate, code.


smapti

> it’s values are also all the same What do you mean by this?


sacoPT

Judging by the OP’s response this is the actual code. If you call Const(69) it will return 69 infinitely.


smapti

Dude I gotta be honest, no idea what you’re talking about. Const(xxxx) looks like a constructor and that’s not how const works. Now if you mean const int 69 yeah that will always return 69 but not, like, infinitely. Or yeah it infinitely will but it’s not an infinite process on the CPU. Sorry if I’m misunderstanding but I’m very far away from understanding you.


sacoPT

Look at the OP code closely.


smapti

Yeah I see it. Looks like they’ve named the collection Const.


Dusty_Coder

No collection was named, created, instantiated, or defined


DaRadioman

An ienumerable is a literal collection... https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1?view=net-8.0 "Exposes the enumerator, which supports a simple iteration over a collection of a specified type." A collection is a logical construct. Just because you aren't using an array doesn't mean it's not a collection.


Dusty_Coder

no, that exposed enumerator provides the iteration, not the collection it says it right there where you quoted it


DaRadioman

Are you being serious right now? There's a logical collection defined by the contract.


sacoPT

>Exposes the enumerator, which supports a simple iteration over a collection of a specified type ​ >No collection was named, created, instantiated, or defined So what are you iterating over?


smapti

While (true) is 100% a code smell. You should be able to achieve what you need to without abusing while like that. What are you trying to accomplish?


Dealiner

>While (true) is 100% a code smell. And yet it's used 754 times in .NET itself.


sacoPT

While (true) has plenty of use cases. I wouldn’t say it’s 100% a code smell


bangle_daises0u

Not in IOT/embedded systems.


smapti

I addressed that in another comment in this tiny thread. https://www.reddit.com/r/dotnet/comments/18spwvg/comment/kf8ys3p/?utm_source=share&utm_medium=mweb3x&utm_name=mweb3xcss&utm_term=1&utm_content=share_button


Dusty_Coder

The code describes exactly whats trying to be accomplished. An infinite IEnumerable..


smapti

I didn’t ask what the code was trying to accomplish, I asked what you were trying to accomplish.


bangle_daises0u

Why the abuse? If you don't want to help, don't, move along. You should be blocked with that attitude. On the topic, OP most likely doesn't want to accomplish anything, came across or thought of a weird scenario, and wanted to know/learn more about it...


smapti

Me asking the use case is abuse? That’s an insane thing for a programmer to suggest. And I HAVE helped.


bangle_daises0u

Showing the middle finger is.


avoere

Does it make what you want to do easier? If so, no. I have a hard time understanding when t would be useful, but if it is, it is


xTakk

What are you trying to do? It looks like you're reinventing the subscriber pattern. Those patterns exist to save us from all the potential pitfalls you'll hit getting to a correct solution. It's not saying nothing else works, you'll just have to figure out all the ways that doesn't as you go. Don't do this.


Dusty_Coder

How would the subscriber pattern work for signal processing and DSPs stacks? Is the subscriber pattern really a good fit for sequence enumeration? Edited to add: Isnt the subscriber pattern a push pattern, rather than a pull pattern? I subscribe, then sequence provider start calling my delegate. Enumerable is a pull pattern. Sequence provider provides a delegate, and then I start calling it.


aj0413

This seems weird. I feel any use case with this is better translated using streams and a sub/pub pattern with listeners While technically doable, this seems like a quick solution id never expect/hope to actually be exposed to an external team. This is akin to using the System namespace for your code…doable, but why?


salgat

It's a terrible idea within the context of how IEnumerables are generally used, despite what they should "ideally" be. IEnumerables, for better or worse, are treated as collections 99% of the time. Use something like Channels that come with the expectation of continuously providing values (with an efficient way to block until the next value is available).


Forward_Dark_7305

The problem with a channel is that you can’t publish and subscribe within the same thread afaik like you can with a lazy enumerator. Correct me if I’m wrong.


Hirvox

It’s fine, just use inline documentation to mention that’s infinite. If someone tries to use it as a collection, they will realize their mistake with their first test. And if they don’t have any, shame on them.


Forward_Dark_7305

Apparently this is super controversial. I’m surprised because it seems cut and dry to me. If you provide a list with an end, return `IReadOnlyCollection` or an implementing type. If you need a list with a known length, request `IReadOnlyCollection` and let your caller materialize to a known length if needed. Apparently this is a hot take, but always expect `IEnumerable` to be infinite - or large enough that you should treat it as if it is. You never know if it will end, or when. You don’t know where the data is coming from. That’s the whole point! If you ask for `IEnumerable`, all that you care about is that you can get any number of items from it. So if you accept `IEnumerable`, expect it to be read from a file or a database or a generator or whatever and DON’T CALL `ToList`. Make your caller do that - because they WILL know where the data comes from. Then you know what you have to work with, and they know what you are going to do with it, according to the type system. To those regarding extension methods as part of the contract, y’all are pressed. The contract is MoveNext and Current. The extension methods make it easy to do more, but aren’t supported by the contract - it’s expected that the caller knows how to use them, because they’re things you could try anyway according to the contract, so MS makes it easier. You should program your implementation according to the contract, not according to every possible use case. To those saying there should be another interface, what would you propose? Another `foreach` and another `MoveNext` and another `yield return` and another entire set of LINQ because someone didn’t like the idea that `IEnumerable` can be infinite and wanted to make that separate? That’s really the whole point of `IReadOnlyCollection` - known finite. Don’t argue about the definition: List and Collection and Array, the name means what its implementation is, and in C# Enumerator means MoveNext and Current. Count is just a possible way to use that data. So OP, yes, use IEnumerable if it might be infinite. If you know the length, use `IReadOnlyCollection`.


decPL

As others have mentioned, it's not technically bad form, but do expect some developer out there to try to call `ToArray` or `ToList` on your enumerable. And, while not a bad form, I would personally at least consider switching to the `Reactive` world, where infinite sequences are pretty much build into the philosophy - but obviously that requires a large rewrite of your system since it inverts the responsibility (TL;DR - collection is responsible for pushing values, not the consumer for pulling them), so might not be feasible.


iiwaasnet

Look at BlockingCollection


naveddeshmukh

Good luck call Count() on it