In thinking about the principle of "be conservative in what you send and liberal in what you accept", I generally try to make my methods receive IEnumerable parameters, but emit a IReadOnlyCollection (except where deferred execution is truly valuable, then I do emit an IEnumerable).

However, I just realized that when specifying methods in an interface, this may be reversed. Is that true?

Should my interface specifications be telling any implementers "I'll give you an IReadOnlyCollection, but you can just give me any old IEnumerable back"? (Again, if deferred execution makes sense, I would provide an IEnumerable as a parameter type instead.) This makes those methods the reverse, accepting a narrow/conservative set but emitting a broad/liberal one. However, this seems right to me because with the dependency inversion, in reality the input arguments when the method is called are really the output arguments of the core system (specifying the interface).

I'm just wondering if my thinking is right on this or not. It seems that making the implementer side as easy as possible is really where the benefit is.

I'm realizing that part of the issue comes from my concern about not requiring implementers to think deeply about whether deferred execution makes sense or not. Knowing when an enumerable will be enumerated can be very important in a system, and with the separation of concerns that an interface can help bring to code, I wouldn't want to be so liberal in what the interface method outputs (which is ultimately an input to the core system) that improperly-deferred IEnumerables get thrown around...

This is not about speed. Not sure why this was raised, but please do not even think for a second about performance implications here. That is not the question.

  • I don't really see any speed or size difference between the two, except one is for readonly and one is for enumeration, both are easy converted to lists. – John Peters Mar 18 '16 at 21:48
  • @JohnPeters This isn't about speed at all. Don't go in that direction. This is about quality software. I care nothing about speed here. – ErikE Mar 18 '16 at 22:22
  • 1
    I've read your post four times now and cannot fully understand what you're asking, how about a code or interface example with an explanation of what its for. Typically interfaces (often used for an API) are just simple contracts telling the user what they are going to get. We know that IQueryable is superior to IEnumerable with respect to "being conservative with what is sent", but even that is dependent on where the enumerable is surfaced, on the caller side or the server side... – John Peters Mar 19 '16 at 0:08
  • @JohnPeters Perhaps someone else can understand. That you thought I was talking about speed when I didn't write one word in there about performance (show me?) gives me some doubts, perhaps someone else might see things differently. – ErikE Mar 19 '16 at 1:51
  • 1
    @CodingGorilla I disagree with you. Developers don't read documentation, and helping them to "fall into the pit of success" rather than failure is only accomplished through the code. If you think the selected data types have no meaning to people, then you and I have fundamentally different philosophies of coding. – ErikE Mar 19 '16 at 4:06

I have similar concerns as you. Technically, you can't guarantee anything about IEnumerable other than that you can enumerate over it once. That may be a benefit--for instance, reading out of a buffered stream. Because the framework didn't support anything less specific than ICollection, I think this became the default for developers who didn't want to expose the ability to add elements to the enumeration. With the advent of .NET 4.5, we finally got the interface that express more properly what's being returned.

Returning IEnumerable for a List isn't wrong, but I do think that returning IReadOnlyCollection is a better choice when you can anticipate the data will be iterated over multiple times. ReSharper, for instance, will warn about multiple iterations over IEnumerable. As a library developer, I think that's more valid than a developer worrying about whether an implementation is deferred or not.

  • I do think that returning IReadOnlyCollection is a better choice when you can anticipate the data will be iterated over multiple times. -- Can you elaborate on why you think that (not saying you're wrong, looking to understand your thinking). – Coding Gorilla Mar 19 '16 at 4:06
  • For instance, how about a method that returns a collection of existing values. You then need to compare that collection to another one to determine what has been added, updated, and removed. You can assume that it will be iterated over multiple times. If the return type is IEnumerable, then it does not follow that this will work. It usually does, but it may not. The IReadOnlyCollection interface is a more restrictive one, which guarantees multiple iterations will return the same results. There's more to the interfaces than just their members. – mgw854 Mar 19 '16 at 4:38
  • Where is it stated that an IEnumerable may or may not support multiple enumerations? Can you point to any concrete implementations of IEnumerable which do not support multiple enumerations? – Coding Gorilla Mar 19 '16 at 4:45
  • Well, the .NET Design Guidelines advocate for using ReadOnlyCollection over IEnumerable, for one. It better expresses the semantics (such as allowing for iteration backward or forward). As for an actual implementation, nothing in the BCL jumps out in my memory, but I've seen code in the wild that does it (or that can only be read forward, like a buffer). msdn.microsoft.com/en-us/library/dn169389(v=vs.110).aspx – mgw854 Mar 19 '16 at 4:59
  • I think you're thinking of a stream, which can be forward only and not seekable, but I thank you for your thoughts and responses. I enjoy hearing how other professionals reason on this stuff (not just trying to be argumentative :) ) – Coding Gorilla Mar 19 '16 at 5:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.