Ковариация и контравариация в универсальных шаблонах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.

    Экземпляр Action<Base> (Action(Of Base) в Visual Basic) можно присвоить переменной типа Action<Derived>.You can assign an instance of Action<Base> (Action(Of Base) in Visual Basic) to a variable of type Action<Derived>.

  • Invariance

    Означает, что можно использовать только изначально заданный тип. Таким образом, параметр инвариантного универсального типа не является ни ковариантным, ни контравариантным.Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

    Экземпляр List<Base> (List(Of Base) в Visual Basic) нельзя присвоить переменной типа List<Derived>, и наоборот.You cannot assign an instance of List<Base> (List(Of Base) in Visual Basic) to a variable of type List<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> (List(Of Derived) в Visual Basic) реализует 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> (Action(Of Base) в Visual Basic), а затем назначает этот делегат переменной типа 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. Лямбда-выражение соответствует делегату, которому присвоено, и определяет метод, который принимает один параметр типа 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>, его аргумент должен быть аргументом типа Derived.When 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. Этот аргумент всегда может быть безопасно передан базовому методу, потому что параметр метода является параметром типа Base.This 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параметры вариантного типа ограничены типами универсального интерфейса и универсального метода-делегата.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.

  • Вариативность применяется только к ссылочным типам; если указать тип значения для параметра вариантного типа, этот параметр типа является инвариантным для типа, созданного в результате.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> (Action(Of Derived) и Action(Of Base) в Visual Basic) нельзя объединять первый делегат со вторым, несмотря на то что результат будет безопасным типом.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> (IEnumerable(Of Base) в Visual Basic) и выводящий эти элементы.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 наследуется от типа Base.Derived inherits from Base. В примере создается пустой список List<Derived> (List(Of Derived) в Visual Basic) и показывается, что этот тип может быть передан методу 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) класс Shape со свойством Area .The example defines an abstract (MustInherit in Visual Basic) Shape class with an Area property. В примере также определяется класс ShapeAreaComparer , реализующий IComparer<Shape> (IComparer(Of Shape) в Visual Basic).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 и переопределяет Area.The Circle class inherits Shape and overrides Area. В примере создается набор SortedSet<T> объектов Circle с помощью конструктора, принимающего IComparer<Circle> (IComparer(Of Circle) в Visual Basic).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>, в примере передается объект ShapeAreaComparer, реализующий IComparer<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

Универсальные делегаты с параметрами вариантного типа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, и еще один класс с методом типа static (Shared в 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> (Func(Of Base, Base) в Visual Basic), так как возвращаемый тип является ковариантным.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> (Func(Of Derived, Derived) в Visual Basic), так как тип параметра является контравариантным.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> (Func(Of Derived, Base) в Visual Basic), объединяя эффекты контравариантного параметра типа и ковариантного возвращаемого типа.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> (Func(Of Base, Derived) в Visual Basic).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). Однако вариативность в привязке делегата работает для всех типов делегата, а не только с типами универсального метода-делегата, имеющего вариантные параметры типа.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 и тип возвращаемого значения Type1, с использованием ковариантности и контрвариантности параметров универсального типа.The 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

Определение вариантных универсальных интерфейсов и делегатов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, среда CLR поддерживает вариантные заметки для параметров универсального типа.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 единственным способом определения универсального класса, имеющего такие заметки, было использование языка MSIL либо путем компиляции класса с помощью программы Ilasm.exe (сборщик MSIL Assembler), либо путем его введения в динамическую сборку.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 (ключевым словом Out в Visual Basic, ключевым словом + для ассемблера MSIL Assembler).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 (ключевым словомIn в Visual Basic, ключевым словом - для ассемблера MSIL Assembler).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.

Параметры вариантного типа могут иметь только типы интерфейса и типы делегата.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 Assembler не выполняет такие проверки, но в случае загрузки типа, нарушающего эти правила, возникает исключение TypeLoadException .The 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

Список вариантных универсальных интерфейсов и типов делегатов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