I understand your thought process, you interfaces are very similar to something I've tried in the past. This guide sums it up though:
Generic Interfaces - Generic classes can implement generic interfaces or closed constructed interfaces as long as the class parameter list supplies all arguments required by the interface
Its all good for them to say that, but in practice it makes sense... Using your definition:
public interface IDbGenericIdRepository<T, TKey> : IDbGenericRepository<T>
where T : HavePrimaryKey<TKey>
Then we are explicitly defining the Generic condition of T
on another generic condition TKey
. That means that we need to explicitly define the type argument TKey
for the compiler to be able to check that the type of T is valid at all.
We have created a dependency here, TKey
and T
are not the same type, T
however must implement a version of HavePrimaryKey
, this interface doesn't care specifically what the actual type of TKey
is, only that the class that implements the interface MUST define it.
You still want to know why though don't you...
Basically where Interface inheritance is concerned, we need to be aware that a class that implements one (or more) interfaces can actually present a different implementation for each member of each interface and a different implementation for the class itself!
So now we'll introduce a simple implementation of HavePrimaryKey<TKey>
and some example classes that implement this interface in different ways, all are valid in the eys of the compiler.
public interface HavePrimaryKey<TKey>
{
T PrimaryKey { get; }
}
public class MemberInfoInt : HavePrimaryKey<int>
{
public int PrimaryKey { get; }
}
public class MemberInfoString : HavePrimaryKey<string>
{
public int PrimaryKey { get; }
string HavePrimaryKey<string> PrimaryKey { get; }
}
Another valid implementation, however absurd is this:
public class MemberInfo3 : HavePrimaryKey<int>, HavePrimaryKey<Guid>
{
public string PrimaryKey { get; }
int HavePrimaryKey<int>.PrimaryKey { get; }
Guid HavePrimaryKey<Guid>.PrimaryKey { get; }
}
So now if we try to implement this using your preferred notation, we can start to see the dilemma: Note, this is not valid
public class Repo : IDbGenericIdRepository<MemberInfo3> ...
In this instance the compiler tries to evaluate typeof(MemberInfo3) == HavePrimaryKey<?>
and it fails there, it cant even compile the expression that is supposed to be used to validate the first expression.
There is no way (other than the one legal way we already know) to tell the compiler which specific type implementation we intend to use to validate that MemberInfo3
is valid to use for a condition for T
.
C# is strongly typed, that's what we all love (or love to hate) about it, so where there is the possibility of ambiguity, it is in our nature to force and expect a strongly typed definition for our types and classes.
That is why you MUST provide all the generic type arguments from the inherited interfaces.