You might want to check co- and contravariance.
You haven't shown us the definition of your DataEtlModelRegistration<T, TResult>
class, but let's imagine that it has a method with signature:
void Accept(T t);
(any method accepting T
as a parameter will do).
Now let's also imagine DerivedEntity
inheriting from BaseEntity
. And now let's change our universe to one in which models.Add(typeof(T), new DataEtlModelRegistration<T, TResult>());
is valid code and call RegisterModelType<DerivedEntity, TAnything>
, where TAnything
can be, well, anything deriving from BaseEntity
.
So an object of type DataEtlModelRegistration<DerivedEntity, TAnything>
is now in the dictionary under the key typeof(DerivedEntity)
. Let's try to extract it:
DataEtlModelRegistration<BaseEntity, BaseEntity> model = models[typeof(DerivedEntity)];
Now, since entity
is of type DataEtlModelRegistration<BaseEntity, BaseEntity>
, this code should work (provided BaseEntity
has an available default ctor):
model.Accept(new BaseEntity());
Bang, the type system is broken. You've passed BaseEntity
to a method that accepts DerivedEntity
as a parameter. BaseEntity
is not DerivedEntity
, you cannot do that.
Because of that, generic types are invariant by default. Basically it means that, e.g. List<DerivedEntity>
is not a List<BaseEntity>
, because you shouldn't be able to add any BaseEntity
to a list of DerivedEntity
s. So, if your class contains a method accepting T
(or TResult
, the same logic applies) as a parameter, you cannot do what you're trying to do.
However, if there is no such method, then you could make your types covariant by using an interface:
interface IModelRegistration<out T, out TResult> where T : BaseEntity where TResult : BaseEntity
{
...
}
internal class DataEtlModelRegistration<T, TResult> : IModelRegistration<T, TResult> where T : BaseEntity where TResult : BaseEntity
{
...
}
Basically you're telling the compiler "Hey, this interface will never accept any objects of the generic types, it only returns them". That code will fail to compile if the interface IModelRegistration
contains a method with T
or TResult
as its parameter. Now it's legal to say:
private readonly Dictionary<Type, IModelRegistration<BaseEntity, BaseEntity>> models = new Dictionary<Type, IModelRegistration<BaseEntity, BaseEntity>>();
models.Add(typeof(DerivedEntity), new DataEtlModelRegistration<DerivedEntity, DerivedEntity>());
You will be able to extract an object from the dictionary as an instance of the IModelRegistration
interface.
IModelRegistration<BaseEntity, BaseEntity> model = models[typeof(DerivedEntity)];
There's no way for you to break the type system now, because we know for a fact that the IModelRegistration
interface has no methods that would accept an object of any of its type parameters.
You can also check out this question, where I gave an explanation for how contravariance works.