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.
You can implement deferred execution for your custom extension methods for IEnumerable using the yield keyword of C#. For example, you can implement custom extension method GetTeenAgerStudents for IEnumerable that returns a list of all students who are teenagers.
It seems confused at first, but it’s worth to remember that deferred execution is a feature of LINQ query, no matter on which data source. LINQ queries are always executed when the query variable is iterated over, not when the query variable is created.
If the underlying type of the IEnumerable collection is an iterator-based implementation generated by LINQ methods like Select or yield in C# or yield statement in Visual Basic, you can fix the violation by converting and caching the collection to another type. However, this allocates extra memory.
Remember, the interface tells you what is expected to be returned. IEnumerable only means you are getting something that can be iterated over (potentially streaming results and making use of deferred execution), and List only means you're getting an in-memory collection that can be added to, removed from, accessed by index, etc.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With