SimpleInjector
SimpleInjector copied to clipboard
Verify null-safety of generic type parameters
In the example below, I have a class with a dependency on a generic interface with a non-nullable type parameter and a class implementing that interface with the same type parameter, but nullable. If I use pure DI to build the object graph, I get a compiler warning about the difference in nullability. However, if I use SimpleInjector to build the object graph, I don't get any warnings at compile time or runtime, leading to possible NullReferenceExceptions in the consumer class when its dependency returns a null that the consumer is not expecting. I would like SimpleInjector to warn me that an implementation of IService<string?> cannot be injected into a consumer expecting IService<string> (given that the type parameter T is not contravariant).
using SimpleInjector;
using var container = new Container();
container.Register<IService<string?>, Service>();
container.Register<Consumer>();
/*
CS8620 Argument of type 'Service' cannot be used for parameter 'service' of type
'IService<string>' in 'Consumer.Consumer(IService<string> service)' due to differences in the
nullability of reference types.
*/
_ = new Consumer(new Service());
// No error
container.Verify();
_ = container.GetInstance<Consumer>();
interface IService<T> { }
class Service : IService<string?> { }
internal class Consumer
{
public Consumer(IService<string> service) { }
}
Currently, there's no way to let Simple Injector warn you about you about this. This is mainly because Simple Injector's generic-type sub system was created way before non-nullable reference types was introduced in C#. But I do absolutely see the use for having such check in the Container, as you currently get a false sense of security.
I don't believe any of the mature DI Containers for .NET can do non-nullable checks for you at the moment, and to be honest, I believe this to be a complex feature to get right, because:
- The generic type system in .NET is quite complicated
- This feature will likely needs to be deeply integrated into Simple Injector's generic-type sub system.
- It requires understanding the difference between in and out type arguments.
- Needs to do this checks for all types of injection that Simple Injector supports, including constructor injection, property injection and decoratee injection.
- It requires both opt-in type checking on the Container level, while requiring opt-out of checking on the registration -or perhaps- interface level.
For now, I'll have to postpone this feature and put it on the backlog. This might be an interesting feature for v6, although it's probably too complex for that version.