To get introduced to the topic, please read Eric Lippert's variance series first.
Here I would like to present my solution to the problem.
After all there are only 3 possibilites:
- Clear covariance (IEnumerator<T>
). - Clear contravariance (IComparer<T>
). - Undecidable (IList<T>
).
All the 3 possibility are easily detectable by compiler and I believe for the first two we would like the compiler will decide the variance automatically. We need a solution for the third case only.
To see the solution, I would like first analyze how we selected which case the given interface belongs to. We analyzed the method signatures in which T is involved:
- If in all of them T is 'out' parameter, the interface is covariant.
- If in all of them T is 'in' parameter, the interface is contravariant.
- If there is a mix - undecidable.
My solution is to say the compiler how I'm going to use my variable and enable either covariant or contravariant invocations:
IList<Animal> aa = whatever;
IList<in Giraffe> ag = aa;
IList<out Giraffe> agX = aa; //fails to compile
ag user sees: 'int ag.IndexOf(Giraffe)' and 'object ag[int]'.
IList<Giraffe> ag = whatever;
IList<out Animal> aa = ag;
IList<in Animal> aaX = ag; //fails to compile
aa user sees: 'int aa.IndexOf(<only null can be passed>
Now I can say in a type safe manner:
class X<T> {
T _t;
void Test(IList<out T> at) {
T t = at[0]; //clearly t is 'out' parameter
}
void Test(IList<in T> at) { //overloaded method!
int i = at.IndexOf(_t); //clearly _t is 'in' parameter
}
}
//...
new X<Animal>().Test(new List<Giraffe>()); //first method is called
new X<Giraffe>().Test(new List<Animal>()); //second method is called
No comments:
Post a Comment