Reputation: 2613
I'm using Ninject v2.2.1.4, so I'm not sure if this is how the newer versions of ninject work.
I'm going to use a classic example of inheritance, Animal. So I have a base abstract Animal
class and then countless implementations of Animal
.
public abstract class Animal { }
public class Tiger : Animal { }
Now I have an IValidator
for Animal
that makes sure the properties are set correctly.
public interface IValidator<T>
{
bool Validate(T object);
}
For every implementation of Animal
(Tiger, Cat, Dog, etc..) I may or may not have an IValidator<T>
implementation. When there isn't a validator for a specific animal, I want to use a default validator that validates the base Animal
properties (IValidator<Animal>
).
So here are a couple implementations of IValidator<T>
, including the default implementation:
// Contains default logic for base Animal inherited by all validators
public abstract class AbstractAnimalValidator<TAnimal> : IValidator<TAnimal> where TAnimal : Animal { }
// Default implementation for IValidator when the animal doesn't have a validator.
//This just calls the base abstract validator logic.
public DefaultAnimalValidator : AbstractAnimalValidator<Animal> { }
// Validator for a Tiger. It calls the base validate method and then validates properties specific to a Tiger.
public TigerValidator : AbstractAnimalValidator<Tiger> { }
Let's just assume that the Tiger is the only animal with its own validator. I would have these two bindings:
Bind<IValidator<Animal>>().To<DefaultAnimalValidator>();
Bind<IValidator<Tiger>>().To<TigerValidator>();
So now I will have logic that tries to inject a validator for the specific animal, by attempting to get an implementation, such as injecting IValidator<Tiger>
. If it doesn't exist, it injects the default validator using IValidator<Animal>
.
The problem is that when trying to inject IValidator<Tiger>
, both of the above bindings match, so I get a multiple bindings found error. This is because Tiger
inherits from Animal
, so Tiger
is both a Tiger
and an Animal
. I've come up with some hacky solutions around this, but is there something I'm missing?
Upvotes: 1
Views: 131
Reputation: 14904
Can you modify the IValidator<T>
interface? If yes, you can mark the type parameter as contravariant:
public interface IValidator<in T>
{
bool Validate(T o);
}
It will allow you to assign an IValidator<Animal>
to IValidator<Tiger>
. Then you can specify all bindings explicitly:
Bind<IValidator<Cat>>().To<DefaultAnimalValidator>();
Bind<IValidator<Dog>>().To<DefaultAnimalValidator>();
Bind<IValidator<Tiger>>().To<TigerValidator>();
This approach has also one big advantage over implicit fallback to IValidator<Animal>
. If you add another class derived from Animal
with it's own validator and forget to add a binding, you will get a run-time error. If you use Bind<IValidator<Animal>>().To<DefaultAnimalValidator>()
, you will get a hard to trace bug.
Upvotes: 1