IEnumerable considered ambiguous
It’s tempting to use IEnumerable<T> liberally in public APIs as a “least common denominator” interface that can represent any collection of objects.
Consider two very different things that could be lurking behind an IEnumerable<T>:
A: An in-memory collection such as an array or list.
B: A lazily evaluated fire hose that hides complex network logic that makes batch requests and yields objects from those batches.
For (A) it’s safe to enumerate the collection multiple times, use the Count() extension, transform with ToList/ToArray, etc. while it could be disastrous to do the same with (B).
How are you supposed to know what you’re getting when someone hands you an IEnumerable<T>? In the vast majority of cases, you’ve probably got (A). Indeed, most programmers will assume this and program against it as an in-memory collection, and they’ll run into trouble (perf problems or worse) when they encounter (B).
Consider a method that takes an IEnumerable<T> as an input. Is it safe to pass a (B) to that method? It’s impossible to know without looking at the implementation.
You could take an IList<T> or an ICollection<T> instead, but these interfaces have mutators on them, which introduces additional ambiguity – does your method modify the collection being passed in?
I’m curious to know what conventions others are using to deal with this ambiguity.