Supporting both covariance and contravariance for

2019-04-07 11:19发布

问题:

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?

回答1:

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.)



回答2:

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.