6

First, I do not consider this question be the dup of these SO questions:

Should I always return IEnumerable<T> instead of IList<T>? and IEnumerable<T> as return type

As we all know ont of the main purposes of introducing several tiers is to decrease coupling.
We must define some interface for data access and our BL should not care about the details of DAL implementation. If mentioned interface returnes IEnumerable<T> BL does not know whether it is just a static IEnumerable or something that has deferred execution. At the same time this particular detail can affect perfromance considerably and requires different coding depending on the implementation.
Well, it is possible to call .ToList() for each IEnumerable in situations when we are going to iterate collection several times. But this decreases perfromance for static collections because of unnecessary new list instantiation.

So I'm trying to understand which approach is better.
More universal and potentially less performant vs More coupled-more performant. I guess, there's no silver bullet but it could be other approaches I've missed.

Community
  • 1
  • 1
Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137
  • Note that if you use foreach to iterate an IEnumerable which is in reality a List or a plain array, it's very fast. The implementation does some optimisations. (You can verify this by using Stopwatch to time things.) – Matthew Watson Jan 28 '13 at 16:21
  • Sure seems like a duplicate to me. Especially of the second question you linked. The accepted answer to that question addresses your concerns quite well. The key is, "it depends." If deferred execution is going to cause a performance problem, then have your DAL return a concrete list. – Jim Mischel Jan 28 '13 at 16:23
  • If you're on .Net 4.5, you can use `IReadOnlyList`. – svick Jan 28 '13 at 17:12
  • @Jimmischel. I agree but in case we want to provide modularity and changeability how can we expect more from the DAL than a mere interface? – Pavel Voronin Jan 28 '13 at 18:14

3 Answers3

8

So I'm trying to understand which approach is better: more universal and less performant vs more coupled and more performant.

First, though that is an interesting tradeoff, in this case I would think that the relevant tradeoff is actually correct vs incorrect, which surely trumps any question of performance. A deferred-execution query usually has the property that it gives you the most up to date results of the query, whereas calling ToList gives you a snapshot of a past version of the query results. There are surely cases where one is correct and the other is incorrect.

Second, assuming that you have dealt with the correctness issue and really do have a performance tradeoff to make, the tradeoff you actually want to make is: more universal and unacceptable performance vs more coupled and acceptable performance, at which is becomes clear that you have to choose the one with acceptable performance.

If both have acceptable performance and one is a few nanoseconds slower than the other, or consumes a few bytes more memory than the other, and both are correct, then who cares which one you choose? Spend your valuable time thinking about something else. And if neither have acceptable performance then you have a bigger problem to solve.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Unfortunately world is not ideal and we have to keep balance between openess of the architecture (the less effort we need to address radical requirements change the better it is) and YAGNI. I think that is a mixture of guessing and experience what helps to tell correct and incorrect apart. Here I'd add that IEnumerable leaves some notion of flexibility concerning data requests. If we treat BL as a CRUD layer for the clients IEnumerable allows them to ask for specific data. Thus we can keep BL simple not bloating it with a bunch of methods for different data read scenarios. – Pavel Voronin Jan 28 '13 at 19:00
  • On the other hand as soon as you wish to move BL to the server the situation becomes the opposite. A set of methods is a great advantage. Now consider that you must refactor old highly couple application to the N-tier app and than move some of its parts to the application server. Next I add that developers of the client apps will be other guys and our team has to respond on their requests quite quickly. And all this a slow evolving process. If I had an experience what is correct and incorrect I wouldn't even ask =) – Pavel Voronin Jan 28 '13 at 19:10
2

If it's important, conceptually, for the caller to know that the return type of the method is a List and not an IEnumerable (for example, to know that there will be no negative consequences for iterating it multiple times), then you should return a List. The point of returning the interface instead is a way of saying, "It doesn't matter what the implementation is." If the implementation does matter, then don't use the interface (in that specific situation).

Servy
  • 202,030
  • 26
  • 332
  • 449
  • " to know that there will be no negative consequences for iterating it multiple times" for IEnumerable. What do you mean ? – Tigran Jan 28 '13 at 16:28
  • @Tigran It's conceivable that an `IEnumerable` could return completely different results when iterated multiple times, or it could involve querying a database or doing computationally intensive tasks. When given an `IEnumerable` from an unknown source it is therefore best to avoid iterating it multiple times at all costs. In certain situations this means putting the sequence into a list, so that you can be sure that iterating it multiple times won't have those (possibly undesirable) side effects. If it's already a list, you have just needlessly done work. – Servy Jan 28 '13 at 16:31
  • 1
    Returning a list has it's own set of worries... such as, did the getter make a defensive copy? – Matthew Watson Jan 28 '13 at 16:33
  • @MatthewWatson Sure. That doesn't mean you shouldn't ever return a list from any method. – Servy Jan 28 '13 at 16:38
  • @Servy: I see, you're talking about *declarative intention*, but the OP, asks about concrete trade offs (possible ones) in its application. So your answer does not *really* answers his question. Declaring IEnumerable, defines an **intention** to define a *stream of data*, but it does not absolutely mean that you can or should not iterate over it multiple times, as it all depends on implementation details. So the question is: how it's better to architect return/and pass values in order **possibly** avoid scallability problems between different layers in the future. – Tigran Jan 28 '13 at 16:38
0

I found good solution: All return types (the in interfaces) in DAL can't be IEnumerable<> (or can't be interface type). But it possible to use in BL. It can be architector's decision.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Alexey Ripenko
  • 139
  • 1
  • 2
  • 7