Possible Duplicate:
Covariance and Contravariance on the same type argument
You can declare a generic type parameter as covariant by using the out
keyword:
interface ICovariant<out R>
You can declare a generic type parameter as contravariant by using the in
keyword:
interface IContravariant<in R>
And you can also support both for different type parameters:
interface IVariant<out R, in A>
So why can't you suport both for a single type parameter?
So why can't you suport both for a single type parameter?
Keep in mind that an interface can only be covariant in a type parameter if that type parameter is output-safe and an interface can only be contravariant in a type parameter if that type parameter is input-safe.
The syntax out T
says that T
is a covariant type parameter.
The syntax in T
says that T
is a contravariant type parameter.
As T
is a covariant type parameter, it is by definition input-unsafe.
As T
is a contravariant type parameter, it is by definition output-unsafe.
Therefore, T
is input-unsafe and output-unsafe.
Consequently, T
is prohibited in input positions, and T
is prohibited in output positions.
Therefore, T
can not appear in input positions nor in any output positions on any methods specified by the interface.
Consequently, T
can not be used on the interface at all, and is pointless as a type parameter. Consequently, the language designers prohibit you from even including such a useless type marked as both covariant and contravariant on the interface to avoid the ugly
interface IFoo<in and out T> { }
Foo<T> : IFoo<T> { }
and then:
IFoo<Cat> cat = (IFoo<Animal>)new Foo<Dog>();
(If you need to read up on input-safe and output-safe, see 13.1.3.1 of the language specification.)
It wouldn't work. Consider this (if in out
existed):
public class INewList<in out T>
{
public T DoIt(T item);
}
This would be impossible to satisfy because people expecting an INewList<T>
would be compatible with interfaces with both narrower and wider types.
Consider INewList<Feline>
:
If in/out both were possible, this interface would be equivalent to INewList<Animal>
but this would be invalid for in positions because it would allow you to widen to type argument:
... DoIt(Animal item)
Which won't work because that would mean you could pass in a new Dog()
instance where a Feline were expected from the original interface.
Similarly in reverse on the out positions because it would allow:
Puma DoIt(...)
Which would be invalid because the original interface could pass back any feline, not necessarily a Puma.