协变性
当SomeType仅描述返回类型参数的操作时,协变性是安全的。
IEnumerable<out T>
接口可能是协变性最常见的例子。它是安全的,因为它只返回类型为T
的值(确切地说是IEnumerator<out T>
),但不接受任何T
对象作为参数。
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
这是可行的,因为
IEnumerator<T>
也是协变的,并且仅返回
T
:
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}
如果您拥有一个名为
Base
的基类和一个名为
Derived
的派生类,则可以执行以下操作:
IEnumerable<Derived> derivedItems = Something();
IEnumerable<Base> baseItems = derivedItems;
这种方法可行是因为
derivedItems
中的每个项目都是
Base
的实例,所以我们刚才这样赋值是完全可以接受的。然而,我们不能反过来赋值:
IEnumerable<Base> baseItems = Something();
IEnumerable<Derived> derivedItems = baseItems; // No good!
这并不安全,因为不能保证每个Base
的实例也是Derived
的实例。
逆变性
当SomeType只描述接受类型参数的操作时,逆变性是安全的。
Action<in T>
委托是逆变性的一个很好的例子。
public delegate void Action<in T>(T obj);
这是安全的,因为它只接受T
作为参数,但不会返回T
。
逆变性让您可以执行以下操作:
Action<Base> baseAction = b => b.DoSomething()
Action<Derived> derivedAction = baseAction
Derived d = new Derived()
// These 2 lines do the same thing:
baseAction(d)
derivedAction(d)
这能够工作是因为将
Derived
的实例传递给
baseAction
是完全可以接受的。然而,反过来就行不通了:
Action<Derived> derivedAction = d => d.DoSomething()
Action<Base> baseAction = derivedAction
Base b = new Base()
baseAction(b)
derivedAction(b)
这并不安全,因为无法保证 Base
的实例也是 Derived
的实例。