Should we add built-in support for injecting arrays and lists?

Coordinator
Oct 24, 2014 at 9:23 AM
Currently, Simple Injector has built-in support for injecting IEnumerable<T>, IReadOnlyCollection<T> and IReadOnlyList<T>. There's currently no support for injecting arrays and IList<T>. The reasoning for not having arrays and IList<T> is that users are not expected to have many dependencies on collections since they should use the composite pattern, and in case you need a dependency on a list, you want to use the most abstract type possible. As explained here.

Downside of this decision however is that people will use this extension method, but in case there's a consumer of such list that is registered with a lifestyle bigger than transient, using that extension method will lead to a lifestyle mismatch warning of the Diagnostic Services, stating something like:
Consumer (Singleton) depends on ILogger[] (Unknown).
The warning is reasonable, because while IEnumerable<T> is a stream which will call back into the container every time it is iterated, an array or list is just a one-time copy of that IEnumerable<T> that gets injected. This means that you need to be careful about the lifestyles of the items in the collection. And since the extension method can't safely determine the lowest lifestyle of the elements in the collection, it gets the lifestyle 'Unknown', and this basically means that you'll get a warning every time there is a consumer with a lifestyle bigger than transient.

But this warning can result in false-positives, for instance when the collection only consists of singletons. This can cause users to discard the warning all together. This however, can lead to the situation where a real lifestyle-mismatch gets undetected, when the collection starts to contain a transient.

So I'm wondering whether it would be good to allow the container to inject arrays and lists as well, because this would allow to integrate this with the diagnostic services, which prevents any false-positives and prevents users from discarding the warning all together.

I'm wondering what counter arguments there are for not supporting this.
Oct 24, 2014 at 9:52 AM
Can you explain why injecting IReadOnlyCollection<T> and IReadOnlyList<T> is supported, while IList<T> is not supported?
Is the lifestyle taken into account when injecting IReadOnlyCollection<T> or IReadOnlyList<T>?
Coordinator
Oct 24, 2014 at 10:14 AM
Just as IEnumerable<T>, IReadOnlyCollection<T> and IReadOnlyList<T> are streams in Simple Injector. That means that every time you do something like collection.First() on an IReadOnlyList<T> that is injected by Simple Injector, the underlying implementation will ask the container to return an instance based on its lifestyle. This means that the injected collection itself is always a singleton and works as a factory/stream.

This same behavior is not possible to implement with array and the concrete List<T> class, since it is impossible to implement this lazy behavior in those types, and caching them is not an option, because they are not immutable. It might be possible with Collection<T>, since this class allows deriving, but doing that would probably lead to behavior that users won't expect.

On the other hand, for the interfaces IList<T> and ICollection<T>, things are different. They are abstractions and the same implementation can be used as is currently done for IReadOnlyCollection<T> and IReadOnlyList<T>. Their implementations can be made immutable. So at least, Simple Injector can easily have support for IList<T> and ICollection<T>.

So the remaining question is, what about allowing injecting of arrays, List<T> and Collection<T>?
Coordinator
Oct 24, 2014 at 10:29 AM
Edited Oct 24, 2014 at 10:45 AM
My first thought on this is that adding this feature would make it easier to migrate to Simple Injector from another container.

The Diagnostic Services can warn for those cases where a collection type is not a stream.
Coordinator
Oct 25, 2014 at 10:09 AM
Edited Oct 25, 2014 at 10:13 AM
I created a work item for adding IList<T> and ICollection<T> here. This is low hanging fruit and can be implemented easily. Allowing arrays, List<T>s and Collection<T>s to be resolved and integrated correctly in the diagnostic system is more complex and might need more thought. Perhaps its even just a bad idea to do so, or we can go with an easy fix, which is registering them as Transient, which means that they will simply always popup in the Diagnostic Services when injected into anything that is not Transient.
Coordinator
Oct 25, 2014 at 3:35 PM
The funny thing is that the current limitation of not injecting IList<T> and ICollection<T> is just artificial. Adding this functionality is technically a matter of changing one line of code :-).

Still think however that it would be much better if users stick to using IEnumerable<T> or IReadOnlyList<T>, since this is much cleaner and much more intent revealing, but I don't think there's any risk in injecting an ICollection<T> or IList<T>, especially when we keep the behavior identical to the currently injected lists. And making it easier to migrate to Simple Injector is a nice bonus, although it should be really easy to migrate to Simple Injector if users applied SOLID and DI best practices to their application :-)
Coordinator
Oct 28, 2014 at 3:40 PM
A new work item has been created for adding array support.