泛型中的协变和逆变Covariance and Contravariance in Generics

协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. 泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. 在引用类型系统时,协变、逆变和不变性具有如下定义。When you are referring to a type system, covariance, contravariance, and invariance have the following definitions. 这些示例假定一个名为 Base 的基类和一个名为 Derived的派生类。The examples assume a base class named Base and a derived class named Derived.

  • Covariance

    使你能够使用比原始指定的类型派生程度更大的类型。Enables you to use a more derived type than originally specified.

    你可以向 IEnumerable<Derived> 类型的变量分配IEnumerable(Of Derived) (在 Visual Basic 中为 IEnumerable<Base>)的实例。You can assign an instance of IEnumerable<Derived> (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable<Base>.

  • Contravariance

    使你能够使用比原始指定的类型更泛型(派生程度更小)的类型。Enables you to use a more generic (less derived) type than originally specified.

    你可以向 IEnumerable<Base> 类型的变量分配IEnumerable(Of Base) (在 Visual Basic 中为 IEnumerable<Derived>)的实例。You can assign an instance of IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) to a variable of type IEnumerable<Derived>.

  • Invariance

    这意味着,你只能使用原始指定的类型;固定泛型类型参数既不是协变类型,也不是逆变类型。Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

    你无法向 IEnumerable<Base> 类型的变量分配 IEnumerable(Of Base)(在 Visual Basic 中为 IEnumerable<Derived>)的实例,反之亦然。You cannot assign an instance of IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) to a variable of type IEnumerable<Derived> or vice versa.

利用协变类型参数,你可以执行非常类似于普通的多形性的分配,如以下代码中所示。Covariant type parameters enable you to make assignments that look much like ordinary Polymorphism, as shown in the following code.

IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
Dim d As IEnumerable(Of Derived) = New List(Of Derived)
Dim b As IEnumerable(Of Base) = d

List<T> 类实现 IEnumerable<T> 接口,因此 List<Derived> (在 Visual Basic 中为List(Of Derived) )实现 IEnumerable<Derived>The List<T> class implements the IEnumerable<T> interface, so List<Derived> (List(Of Derived) in Visual Basic) implements IEnumerable<Derived>. 协变类型参数将执行其余的工作。The covariant type parameter does the rest.

相反,逆变看起来却不够直观。Contravariance, on the other hand, seems counterintuitive. 下面的示例创建类型 Action<Base> (在 Visual Basic 中为Action(Of Base) )的委托,然后将此委托分配给类型 Action<Derived>的变量。The following example creates a delegate of type Action<Base> (Action(Of Base) in Visual Basic), and then assigns that delegate to a variable of type Action<Derived>.

Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());
Dim b As Action(Of Base) = Sub(target As Base) 
                               Console.WriteLine(target.GetType().Name)
                           End Sub
Dim d As Action(Of Derived) = b
d(New Derived())

此示例看起来是倒退了,但它是可编译和运行的类型安全代码。This seems backward, but it is type-safe code that compiles and runs. 由于 lambda 表达式与其自身所分配到的委托相匹配,因此它会定义一个方法,此方法采用一个类型 Base 的参数且没有返回值。The lambda expression matches the delegate it is assigned to, so it defines a method that takes one parameter of type Base and that has no return value. 可以将结果委托分配给类型类型 Action<Derived> 的变量,因为 T 委托的类型参数 Action<T> 是逆变类型参数。The resulting delegate can be assigned to a variable of type Action<Derived> because the type parameter T of the Action<T> delegate is contravariant. 由于 T 指定了一个参数类型,因此该代码是类型安全代码。The code is type-safe because T specifies a parameter type. 在调用类型 Action<Base> 的委托(就像它是类型 Action<Derived>的委托一样)时,其参数必须属于类型 DerivedWhen the delegate of type Action<Base> is invoked as if it were a delegate of type Action<Derived>, its argument must be of type Derived. 始终可以将此实参安全地传递给基础方法,因为该方法的形参属于类型 BaseThis argument can always be passed safely to the underlying method, because the method's parameter is of type Base.

通常,协变类型参数可用作委托的返回类型,而逆变类型参数可用作参数类型。In general, a covariant type parameter can be used as the return type of a delegate, and contravariant type parameters can be used as parameter types. 对于接口,协变类型参数可用作接口的方法的返回类型,而逆变类型参数可用作接口的方法的参数类型。For an interface, covariant type parameters can be used as the return types of the interface's methods, and contravariant type parameters can be used as the parameter types of the interface's methods.

协变和逆变统称为“变体”。Covariance and contravariance are collectively referred to as variance. 未标记为协变或逆变的泛型类型参数称为“固定参数” 。A generic type parameter that is not marked covariant or contravariant is referred to as invariant. 有关公共语言运行时中变体的事项的简短摘要:A brief summary of facts about variance in the common language runtime:

  • .NET Framework 4.NET Framework 4中,Variant 类型参数仅限于泛型接口和泛型委托类型。In the .NET Framework 4.NET Framework 4, variant type parameters are restricted to generic interface and generic delegate types.

  • 泛型接口或泛型委托类型可以同时具有协变和逆变类型参数。A generic interface or generic delegate type can have both covariant and contravariant type parameters.

  • 变体仅适用于引用类型;如果为 Variant 类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.

  • 变体不适用于委托组合。Variance does not apply to delegate combination. 也就是说,在给定类型 Action<Derived>Action<Base> (在 Visual Basic 中为Action(Of Derived)Action(Of Base) )的两个委托的情况下,无法将第二个委托与第一个委托结合起来,尽管结果将是类型安全的。That is, given two delegates of types Action<Derived> and Action<Base> (Action(Of Derived) and Action(Of Base) in Visual Basic), you cannot combine the second delegate with the first although the result would be type safe. 变体允许将第二个委托分配给类型 Action<Derived>的变量,但只能在这两个委托的类型完全匹配的情况下对它们进行组合。Variance allows the second delegate to be assigned to a variable of type Action<Derived>, but delegates can combine only if their types match exactly.

以下各小节将详细介绍协变和逆变类型参数:The following subsections describe covariant and contravariant type parameters in detail:

具有协变类型参数的泛型接口Generic Interfaces with Covariant Type Parameters

.NET Framework 4.NET Framework 4开始,某些泛型接口具有协变类型参数;例如: IEnumerable<T>IEnumerator<T>IQueryable<T>IGrouping<TKey,TElement>Starting with the .NET Framework 4.NET Framework 4, several generic interfaces have covariant type parameters; for example: IEnumerable<T>, IEnumerator<T>, IQueryable<T>, and IGrouping<TKey,TElement>. 由于这些接口的所有类型参数都是协变类型参数,因此这些类型参数只用于成员的返回类型。All the type parameters of these interfaces are covariant, so the type parameters are used only for the return types of the members.

下面的示例阐释了协变类型参数。The following example illustrates covariant type parameters. 此示例定义了两个类型: Base 具有一个名为 PrintBases 的静态方法,该方法采用 IEnumerable<Base> (在 Visual Basic 中为IEnumerable(Of Base) )并输出元素。The example defines two types: Base has a static method named PrintBases that takes an IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) and prints the elements. Derived 继承自 BaseDerived inherits from Base. 此示例创建一个空 List<Derived> (在 Visual Basic 中为List(Of Derived) ),并且说明可以将该类型传递给 PrintBases 且在不进行强制转换的情况下将该类型分配给类型 IEnumerable<Base> 的变量。The example creates an empty List<Derived> (List(Of Derived) in Visual Basic) and demonstrates that this type can be passed to PrintBases and assigned to a variable of type IEnumerable<Base> without casting. List<T> 实现 IEnumerable<T>,它具有一个协变类型参数。List<T> implements IEnumerable<T>, which has a single covariant type parameter. 协变类型参数是可使用 IEnumerable<Derived> 的实例而非 IEnumerable<Base>的原因。The covariant type parameter is the reason why an instance of IEnumerable<Derived> can be used instead of IEnumerable<Base>.

using System;
using System.Collections.Generic;

class Base
{
    public static void PrintBases(IEnumerable<Base> bases)
    {
        foreach(Base b in bases)
        {
            Console.WriteLine(b);
        }
    }
}

class Derived : Base
{
    public static void Main()
    {
        List<Derived> dlist = new List<Derived>();

        Derived.PrintBases(dlist);
        IEnumerable<Base> bIEnum = dlist;
    }
}
Imports System.Collections.Generic

Class Base
    Public Shared Sub PrintBases(ByVal bases As IEnumerable(Of Base))
        For Each b As Base In bases
            Console.WriteLine(b)
        Next
    End Sub    
End Class

Class Derived
    Inherits Base

    Shared Sub Main()
        Dim dlist As New List(Of Derived)()

        Derived.PrintBases(dlist)
        Dim bIEnum As IEnumerable(Of Base) = dlist
    End Sub
End Class

返回页首Back to top

具有逆变泛型类型参数的泛型接口Generic Interfaces with Contravariant Generic Type Parameters

.NET Framework 4.NET Framework 4开始,某些泛型接口具有逆变类型参数;例如: IComparer<T>IComparable<T>IEqualityComparer<T>Starting with the .NET Framework 4.NET Framework 4, several generic interfaces have contravariant type parameters; for example: IComparer<T>, IComparable<T>, and IEqualityComparer<T>. 由于这些接口只具有逆变类型参数,因此这些类型参数只用作接口成员中的参数类型。These interfaces have only contravariant type parameters, so the type parameters are used only as parameter types in the members of the interfaces.

下面的示例阐释了逆变类型参数。The following example illustrates contravariant type parameters. 该示例定义具有MustInherit 属性的抽象(在 Visual Basic 中为 ShapeArea 类。The example defines an abstract (MustInherit in Visual Basic) Shape class with an Area property. 该示例还定义一个实现 ShapeAreaComparer (在 Visual Basic 中为 IComparer<Shape> )的IComparer(Of Shape) 类。The example also defines a ShapeAreaComparer class that implements IComparer<Shape> (IComparer(Of Shape) in Visual Basic). IComparer<T>.Compare 方法的实现基于 Area 属性的值,所以 ShapeAreaComparer 可用于按区域对 Shape 对象排序。The implementation of the IComparer<T>.Compare method is based on the value of the Area property, so ShapeAreaComparer can be used to sort Shape objects by area.

Circle 类继承 Shape 并重写 AreaThe Circle class inherits Shape and overrides Area. 该示例创建 SortedSet<T> 对象的 Circle ,使用采用 IComparer<Circle> (在 Visual Basic 中为IComparer(Of Circle) )的构造函数。The example creates a SortedSet<T> of Circle objects, using a constructor that takes an IComparer<Circle> (IComparer(Of Circle) in Visual Basic). 但是,该对象不传递 IComparer<Circle>,而是传递一个用于实现 ShapeAreaComparerIComparer<Shape> 对象。However, instead of passing an IComparer<Circle>, the example passes a ShapeAreaComparer object, which implements IComparer<Shape>. 当代码需要派生程度较大的类型的比较器 (Shape) 时,该示例可以传递派生程度较小的类型的比较器 (Circle),因为 IComparer<T> 泛型接口的类型参数是逆变参数。The example can pass a comparer of a less derived type (Shape) when the code calls for a comparer of a more derived type (Circle), because the type parameter of the IComparer<T> generic interface is contravariant.

Circle 中添加新 SortedSet<Circle>对象时,每次将新元素与现有元素进行比较时,都会调用 IComparer<Shape>.Compare 对象的IComparer(Of Shape).Compare 方法(在 Visual Basic 中为 ShapeAreaComparer 方法)。When a new Circle object is added to the SortedSet<Circle>, the IComparer<Shape>.Compare method (IComparer(Of Shape).Compare method in Visual Basic) of the ShapeAreaComparer object is called each time the new element is compared to an existing element. 方法 (Shape) 的参数类型比被传递的类型 (Circle) 的派生程度小,所以调用是类型安全的。The parameter type of the method (Shape) is less derived than the type that is being passed (Circle), so the call is type safe. 逆变使 ShapeAreaComparer 可以对派生自 Shape的任意单个类型的集合以及混合类型的集合排序。Contravariance enables ShapeAreaComparer to sort a collection of any single type, as well as a mixed collection of types, that derive from Shape.

using System;
using System.Collections.Generic;

abstract class Shape
{
    public virtual double Area { get { return 0; }}
}

class Circle : Shape
{
    private double r;
    public Circle(double radius) { r = radius; }
    public double Radius { get { return r; }}
    public override double Area { get { return Math.PI * r * r; }}
}

class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>
{
    int IComparer<Shape>.Compare(Shape a, Shape b) 
    { 
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : a.Area.CompareTo(b.Area);
    }
}

class Program
{
    static void Main()
    {
        // You can pass ShapeAreaComparer, which implements IComparer<Shape>,
        // even though the constructor for SortedSet<Circle> expects 
        // IComparer<Circle>, because type parameter T of IComparer<T> is
        // contravariant.
        SortedSet<Circle> circlesByArea = 
            new SortedSet<Circle>(new ShapeAreaComparer()) 
                { new Circle(7.2), new Circle(100), null, new Circle(.01) };

        foreach (Circle c in circlesByArea)
        {
            Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);
        }
    }
}

/* This code example produces the following output:

null
Circle with area 0.000314159265358979
Circle with area 162.860163162095
Circle with area 31415.9265358979
 */
Imports System.Collections.Generic

MustInherit Class Shape
    Public MustOverride ReadOnly Property Area As Double
End Class

Class Circle
    Inherits Shape

    Private r As Double 
    Public Sub New(ByVal radius As Double)
        r = radius
    End Sub 
    Public ReadOnly Property Radius As Double
        Get
            Return r
        End Get
    End Property
    Public Overrides ReadOnly Property Area As Double
        Get
            Return Math.Pi * r * r
        End Get
    End Property
End Class

Class ShapeAreaComparer
    Implements System.Collections.Generic.IComparer(Of Shape)

    Private Function AreaComparer(ByVal a As Shape, ByVal b As Shape) As Integer _
            Implements System.Collections.Generic.IComparer(Of Shape).Compare
        If a Is Nothing Then Return If(b Is Nothing, 0, -1)
        Return If(b Is Nothing, 1, a.Area.CompareTo(b.Area))
    End Function
End Class

Class Program
    Shared Sub Main()
        ' You can pass ShapeAreaComparer, which implements IComparer(Of Shape),
        ' even though the constructor for SortedSet(Of Circle) expects 
        ' IComparer(Of Circle), because type parameter T of IComparer(Of T)
        ' is contravariant.
        Dim circlesByArea As New SortedSet(Of Circle)(New ShapeAreaComparer()) _
            From { New Circle(7.2), New Circle(100), Nothing, New Circle(.01) }

        For Each c As Circle In circlesByArea
            Console.WriteLine(If(c Is Nothing, "Nothing", "Circle with area " & c.Area))
        Next
    End Sub
End Class

' This code example produces the following output:
'
'Nothing
'Circle with area 0.000314159265358979
'Circle with area 162.860163162095
'Circle with area 31415.9265358979

返回页首Back to top

具有 Variant 类型参数的泛型委托Generic Delegates with Variant Type Parameters

.NET Framework 4.NET Framework 4中, Func 泛型委托(如 Func<T,TResult>)具有协变返回类型和逆变参数类型。In the .NET Framework 4.NET Framework 4, the Func generic delegates, such as Func<T,TResult>, have covariant return types and contravariant parameter types. Action 泛型委托(如 Action<T1,T2>)具有逆变参数类型。The Action generic delegates, such as Action<T1,T2>, have contravariant parameter types. 这意味着,可以将委托指派给具有派生程度较高的参数类型和(对于 Func 泛型委托)派生程度较低的返回类型的变量。This means that the delegates can be assigned to variables that have more derived parameter types and (in the case of the Func generic delegates) less derived return types.

备注

Func 泛型委托的最后一个泛型类型参数指定委托签名中返回值的类型。The last generic type parameter of the Func generic delegates specifies the type of the return value in the delegate signature. 该参数是协变的(out 关键字),而其他泛型类型参数是逆变的(in 关键字)。It is covariant (out keyword), whereas the other generic type parameters are contravariant (in keyword).

下面的代码阐释这一点。The following code illustrates this. 第一段代码定义了一个名为 Base的类、一个名为 Derived 的类(此类继承 Base)和另一个具有名为 staticShared 方法(在 Visual Basic 中为 MyMethod)的类。The first piece of code defines a class named Base, a class named Derived that inherits Base, and another class with a static method (Shared in Visual Basic) named MyMethod. 该方法采用 Base 的实例,并返回 Derived 的实例。The method takes an instance of Base and returns an instance of Derived. (如果参数是 Derived 的实例,则 MyMethod 将返回该实例;如果参数是 Base 的实例,则 MyMethod 将返回 Derived 的新实例。)在 Main() 中,该示例创建一个表示 Func<Base, Derived>Func(Of Base, Derived)(在 Visual Basic 中为 MyMethod)的实例,并将此实例存储在变量 f1 中。(If the argument is an instance of Derived, MyMethod returns it; if the argument is an instance of Base, MyMethod returns a new instance of Derived.) In Main(), the example creates an instance of Func<Base, Derived> (Func(Of Base, Derived) in Visual Basic) that represents MyMethod, and stores it in the variable f1.

public class Base {}
public class Derived : Base {}

public class Program
{
    public static Derived MyMethod(Base b)
    {
        return b as Derived ?? new Derived();
    }

    static void Main() 
    {
        Func<Base, Derived> f1 = MyMethod;
Public Class Base 
End Class
Public Class Derived
    Inherits Base
End Class

Public Class Program
    Public Shared Function MyMethod(ByVal b As Base) As Derived 
        Return If(TypeOf b Is Derived, b, New Derived())
    End Function

    Shared Sub Main() 
        Dim f1 As Func(Of Base, Derived) = AddressOf MyMethod

第二段代码说明可以将委托分配给类型 Func<Base, Base> (在 Visual Basic 中为Func(Of Base, Base) )的变量,因为返回类型是协变的。The second piece of code shows that the delegate can be assigned to a variable of type Func<Base, Base> (Func(Of Base, Base) in Visual Basic), because the return type is covariant.

// Covariant return type.
Func<Base, Base> f2 = f1;
Base b2 = f2(new Base());
' Covariant return type.
Dim f2 As Func(Of Base, Base) = f1
Dim b2 As Base = f2(New Base())

第三段代码说明可以将委托分配给类型 Func<Derived, Derived> (在 Visual Basic 中为Func(Of Derived, Derived) )的变量,因为参数类型是逆变的。The third piece of code shows that the delegate can be assigned to a variable of type Func<Derived, Derived> (Func(Of Derived, Derived) in Visual Basic), because the parameter type is contravariant.

// Contravariant parameter type.
Func<Derived, Derived> f3 = f1;
Derived d3 = f3(new Derived());
' Contravariant parameter type.
Dim f3 As Func(Of Derived, Derived) = f1
Dim d3 As Derived = f3(New Derived())

最后一段代码说明可以将委托分配给类型 Func<Derived, Base> (在 Visual Basic 中为Func(Of Derived, Base) )的变量,从而将逆变参数类型和协变返回类型的作用结合起来。The final piece of code shows that the delegate can be assigned to a variable of type Func<Derived, Base> (Func(Of Derived, Base) in Visual Basic), combining the effects of the contravariant parameter type and the covariant return type.

// Covariant return type and contravariant parameter type.
Func<Derived, Base> f4 = f1;
Base b4 = f4(new Derived());
' Covariant return type and contravariant parameter type.
Dim f4 As Func(Of Derived, Base) = f1
Dim b4 As Base = f4(New Derived())

泛型委托和非泛型委托中的变体Variance in Generic and Non-Generic Delegates

在上面的代码中, MyMethod 的签名与所构造的泛型委托 Func<Base, Derived> (在 Visual Basic 中为Func(Of Base, Derived) )的签名完全匹配。In the preceding code, the signature of MyMethod exactly matches the signature of the constructed generic delegate: Func<Base, Derived> (Func(Of Base, Derived) in Visual Basic). 此示例说明,只要所有委托类型都是从泛型委托类型 Func<T,TResult>构造的,就可以将此泛型委托存储在具有派生程度更大的参数类型和派生程度更小的返回类型的变量或方法参数中。The example shows that this generic delegate can be stored in variables or method parameters that have more derived parameter types and less derived return types, as long as all the delegate types are constructed from the generic delegate type Func<T,TResult>.

这一点非常重要。This is an important point. 协变和逆变在泛型委托的类型参数中的作用与协变和逆变在普通的委托绑定中的作用类似(请参阅委托中的变体)。The effects of covariance and contravariance in the type parameters of generic delegates are similar to the effects of covariance and contravariance in ordinary delegate binding (see Variance in Delegates). 但是,委托绑定中的变化适用于所有委托类型,而不仅仅适用于具有 Variant 类型参数的泛型委托类型。However, variance in delegate binding works with all delegate types, not just with generic delegate types that have variant type parameters. 此外,通过委托绑定中的变化,可以将方法绑定到具有限制较多的参数类型和限制较少的返回类型的任何委托,而对于泛型委托的指派,只有在委托类型是基于同一个泛型类型定义构造的时才可以进行。Furthermore, variance in delegate binding enables a method to be bound to any delegate that has more restrictive parameter types and a less restrictive return type, whereas the assignment of generic delegates works only if both delegate types are constructed from the same generic type definition.

下面的示例演示委托绑定中的变化和泛型类型参数中的变化的组合效果。The following example shows the combined effects of variance in delegate binding and variance in generic type parameters. 该示例定义了一个类型层次结构,其中包含三个按派生程度从低到高排列的类型,即Type1的派生程度最低,Type3的派生程度最高。The example defines a type hierarchy that includes three types, from least derived (Type1) to most derived (Type3). 普通委托绑定中的变化用于将参数类型为 Type1 、返回类型为 Type3 的方法绑定到参数类型为 Type2 、返回类型为 Type2的泛型委托。Variance in ordinary delegate binding is used to bind a method with a parameter type of Type1 and a return type of Type3 to a generic delegate with a parameter type of Type2 and a return type of Type2. 然后,使用泛型类型参数的协变和逆变,将得到的泛型委托指派给另一个变量,此变量的泛型委托类型的参数类型为 Type3 ,返回类型为 Type1The resulting generic delegate is then assigned to another variable whose generic delegate type has a parameter of type Type3 and a return type of Type1, using the covariance and contravariance of generic type parameters. 第二个指派要求变量类型和委托类型是基于同一个泛型类型定义(在本例中为 Func<T,TResult>)构造的。The second assignment requires both the variable type and the delegate type to be constructed from the same generic type definition, in this case, Func<T,TResult>.

using System;

public class Type1 {}
public class Type2 : Type1 {}
public class Type3 : Type2 {}

public class Program
{
    public static Type3 MyMethod(Type1 t)
    {
        return t as Type3 ?? new Type3();
    }

    static void Main() 
    {
        Func<Type2, Type2> f1 = MyMethod;

        // Covariant return type and contravariant parameter type.
        Func<Type3, Type1> f2 = f1;
        Type1 t1 = f2(new Type3());
    }
}
Public Class Type1 
End Class
Public Class Type2
    Inherits Type1
End Class
Public Class Type3
    Inherits Type2
End Class

Public Class Program
    Public Shared Function MyMethod(ByVal t As Type1) As Type3
        Return If(TypeOf t Is Type3, t, New Type3())
    End Function

    Shared Sub Main() 
        Dim f1 As Func(Of Type2, Type2) = AddressOf MyMethod

        ' Covariant return type and contravariant parameter type.
        Dim f2 As Func(Of Type3, Type1) = f1
        Dim t1 As Type1 = f2(New Type3())
    End Sub
End Class

返回页首Back to top

定义 Variant 泛型接口和委托Defining Variant Generic Interfaces and Delegates

.NET Framework 4.NET Framework 4开始,Visual Basic 和 C# 提供了一些关键字,利用这些关键字,可以将接口和委托的泛型类型参数标记为协变或逆变。Starting with the .NET Framework 4.NET Framework 4, Visual Basic and C# have keywords that enable you to mark the generic type parameters of interfaces and delegates as covariant or contravariant.

备注

从 .NET Framework 2.0 版开始,公共语言运行时支持泛型类型参数上的变化批注。Starting with the .NET Framework version 2.0, the common language runtime supports variance annotations on generic type parameters. .NET Framework 4.NET Framework 4 之前,定义包含这些批注的泛型类的唯一方法就是利用 Ilasm.exe(IL 汇编程序) 编译该类或在动态程序集中发出该类,从而使用 Microsoft 中间语言 (MSIL)。Prior to the .NET Framework 4.NET Framework 4, the only way to define a generic class that has these annotations is to use Microsoft intermediate language (MSIL), either by compiling the class with Ilasm.exe (IL Assembler) or by emitting it in a dynamic assembly.

协变类型参数用 out 关键字(在 Visual Basic 中为 Out 关键字,在 MSIL 汇编程序中为 +)标记。A covariant type parameter is marked with the out keyword (Out keyword in Visual Basic, + for the MSIL Assembler). 可以将协变类型参数用作属于接口的方法的返回值,或用作委托的返回类型。You can use a covariant type parameter as the return value of a method that belongs to an interface, or as the return type of a delegate. 但不能将协变类型参数用作接口方法的泛型类型约束。You cannot use a covariant type parameter as a generic type constraint for interface methods.

备注

如果接口的方法具有泛型委托类型的参数,则接口类型的协变类型参数可用于指定委托类型的逆变类型参数。If a method of an interface has a parameter that is a generic delegate type, a covariant type parameter of the interface type can be used to specify a contravariant type parameter of the delegate type.

逆变类型参数用 in 关键字(在 Visual Basic 中为In 关键字,在 - MSIL 汇编程序 中为)标记。A contravariant type parameter is marked with the in keyword (In keyword in Visual Basic, - for the MSIL Assembler). 可以将逆变类型参数用作属于接口的方法的参数类型,或用作委托的参数类型。You can use a contravariant type parameter as the type of a parameter of a method that belongs to an interface, or as the type of a parameter of a delegate. 也可以将逆变类型参数用作接口方法的泛型类型约束。You can use a contravariant type parameter as a generic type constraint for an interface method.

只有接口类型和委托类型才能具有 Variant 类型参数。Only interface types and delegate types can have variant type parameters. 接口或委托类型可以同时具有协变和逆变类型参数。An interface or delegate type can have both covariant and contravariant type parameters.

Visual Basic 和 C# 不允许违反协变和逆变类型参数的使用规则,也不允许将协变和逆变批注添加到接口和委托类型之外的类型参数中。Visual Basic and C# do not allow you to violate the rules for using covariant and contravariant type parameters, or to add covariance and contravariance annotations to the type parameters of types other than interfaces and delegates. MSIL 汇编程序 不执行此类检查,但如果你尝试加载违反规则的类型,则会引发 TypeLoadExceptionThe MSIL Assembler does not perform such checks, but a TypeLoadException is thrown if you try to load a type that violates the rules.

有关更多信息和示例代码,请参阅泛型接口中的变体For information and example code, see Variance in Generic Interfaces.

返回页首Back to top

Variant 泛型接口和委托类型的列表List of Variant Generic Interface and Delegate Types

.NET Framework 4.NET Framework 4中,下面的接口和委托类型具有协变和/或逆变类型参数。In the .NET Framework 4.NET Framework 4, the following interface and delegate types have covariant and/or contravariant type parameters.

类型Type 协变类型参数Covariant type parameters 逆变类型参数Contravariant type parameters
Action<T>Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>Action<T> to Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16> Yes
Comparison<T> Yes
Converter<TInput,TOutput> Yes Yes
Func<TResult> Yes
Func<T,TResult>Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>Func<T,TResult> to Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> Yes Yes
IComparable<T> Yes
Predicate<T> Yes
IComparer<T> Yes
IEnumerable<T> Yes
IEnumerator<T> Yes
IEqualityComparer<T> Yes
IGrouping<TKey,TElement> Yes
IOrderedEnumerable<TElement> Yes
IOrderedQueryable<T> Yes
IQueryable<T> Yes

请参阅See Also

协变和逆变 (C#)Covariance and Contravariance (C#)
协变和逆变 (Visual Basic) Covariance and Contravariance (Visual Basic)
委托中的变体Variance in Delegates