Covarianza y contravarianza en genéricosCovariance and Contravariance in Generics

Covarianza y contravarianza son términos que hacen referencia a la capacidad de usar un tipo más derivado (más específico) o menos derivado (menos específico) que el indicado originalmente.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. Los parámetros de tipo genérico admiten la covarianza y contravarianza para proporcionar mayor flexibilidad a la hora de asignar y usar tipos genéricos.Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. Cuando se hace referencia a un sistema de tipos, la covarianza, contravarianza e invarianza tienen las siguientes definiciones.When you are referring to a type system, covariance, contravariance, and invariance have the following definitions. En el ejemplo se presupone una clase base denominada Base y una clase derivada denominada Derived.The examples assume a base class named Base and a derived class named Derived.

  • Covariance

    Permite usar un tipo más derivado que el especificado originalmente.Enables you to use a more derived type than originally specified.

    Puede asignar una instancia de IEnumerable<Derived> (IEnumerable(Of Derived) en Visual Basic) a una variable de tipo IEnumerable<Base>.You can assign an instance of IEnumerable<Derived> (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable<Base>.

  • Contravariance

    Permite usar un tipo más genérico (menos derivado) que el especificado originalmente.Enables you to use a more generic (less derived) type than originally specified.

    Puede asignar una instancia de Action<Base> (Action(Of Base) en Visual Basic) a una variable de tipo Action<Derived>.You can assign an instance of Action<Base> (Action(Of Base) in Visual Basic) to a variable of type Action<Derived>.

  • Invariance

    Significa que solo se puede usar el tipo especificado originalmente. Así, un parámetro de tipo genérico invariable no es covariante ni contravariante.Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

    No se puede asignar una instancia de List<Base> (List(Of Base) en Visual Basic) a una variable de tipo List<Derived> o viceversa.You cannot assign an instance of List<Base> (List(Of Base) in Visual Basic) to a variable of type List<Derived> or vice versa.

Los parámetros de tipo covariante permiten realizar asignaciones muy similares al polimorfismo, como se muestra en el código siguiente.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

La clase List<T> implementa la interfaz IEnumerable<T> , por lo que List<Derived> (List(Of Derived) en Visual Basic) implementa IEnumerable<Derived>.The List<T> class implements the IEnumerable<T> interface, so List<Derived> (List(Of Derived) in Visual Basic) implements IEnumerable<Derived>. El parámetro de tipo covariante se encarga del resto.The covariant type parameter does the rest.

La contravarianza, sin embargo, parece poco intuitiva.Contravariance, on the other hand, seems counterintuitive. En el siguiente ejemplo, se crea un delegado de tipo Action<Base> (Action(Of Base) en Visual Basic) y, a continuación, se asigna ese delegado a una variable de tipo 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())

Parece un paso hacia atrás, pero lo que se compila y se ejecuta es código con seguridad de tipos.This seems backward, but it is type-safe code that compiles and runs. La expresión lambda se corresponde con el delegado que tiene asignado, por lo que define un método que toma un parámetro de tipo Base y no tiene ningún valor devuelto.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. El delegado resultante puede asignarse a una variable de tipo Action<Derived> porque el parámetro de tipo T del delegado Action<T> es contravariante.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. El código tiene seguridad de tipos porque T especifica un tipo de parámetro.The code is type-safe because T specifies a parameter type. Cuando se invoca el delegado de tipo Action<Base> como si fuera un delegado de tipo Action<Derived>, su argumento debe ser de tipo 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. Este argumento siempre se puede pasar de manera segura al método subyacente porque el parámetro del método es de tipo Base.This argument can always be passed safely to the underlying method, because the method's parameter is of type Base.

En general, los parámetros de tipo covariante se pueden utilizar como tipos de valor devuelto de un delegado, y los parámetros de tipo contravariante se pueden usar como tipos de parámetro.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. En el caso de una interfaz, los parámetros de tipo covariante se pueden utilizar como tipos de valor devuelto de los métodos de la interfaz, y los parámetros de tipo contravariante se pueden usar como tipos de parámetro de los métodos de la interfaz.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.

La covarianza y la contravarianza se denominan colectivamente varianza.Covariance and contravariance are collectively referred to as variance. Un parámetro de tipo genérico que no está marcado como covariante ni contravariante se denomina invariable.A generic type parameter that is not marked covariant or contravariant is referred to as invariant. Un breve resumen de hechos relacionados con la varianza en Common Language Runtime:A brief summary of facts about variance in the common language runtime:

  • En .NET Framework 4.NET Framework 4, los parámetros de tipo variante están restringidos a los tipos de interfaz genérica y delegado genérico.In the .NET Framework 4.NET Framework 4, variant type parameters are restricted to generic interface and generic delegate types.

  • Un tipo de interfaz genérica o de delegado genérico puede tener parámetros de tipo covariante y contravariante.A generic interface or generic delegate type can have both covariant and contravariant type parameters.

  • La varianza se aplica únicamente a los tipos de referencia; si se especifica un tipo de valor para un parámetro de tipo variante, ese parámetro de tipo es invariable para el tipo construido resultante.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.

  • La varianza no se aplica a la combinación de delegados.Variance does not apply to delegate combination. Es decir, si hay dos delegados de tipo Action<Derived> y de tipo Action<Base> (Action(Of Derived) y Action(Of Base) en Visual Basic), no se puede combinar el segundo delegado con el primero aunque el resultado tuviese seguridad de tipos.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. La varianza permite la asignación del segundo delegado a una variable de tipo Action<Derived>, pero los delegados solo se pueden combinar si tienen exactamente el mismo tipo.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.

En las próximas subsecciones se describen los parámetros de tipo covariante y contravariante en detalle:The following subsections describe covariant and contravariant type parameters in detail:

Interfaces genéricas con parámetros de tipo covarianteGeneric Interfaces with Covariant Type Parameters

A partir de .NET Framework 4.NET Framework 4, varias interfaces genéricas tienen parámetros de tipo covariante; por ejemplo: IEnumerable<T>, IEnumerator<T>, IQueryable<T>y 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>. Todos los parámetros de tipo de estas interfaces son covariantes, por lo que los parámetros de tipo se usan únicamente para los tipos de valor devuelto de los miembros.All the type parameters of these interfaces are covariant, so the type parameters are used only for the return types of the members.

En el ejemplo siguiente, se muestran los parámetros de tipo covariante.The following example illustrates covariant type parameters. Se definen dos tipos: Base tiene un método estático denominado PrintBases que toma una interfaz IEnumerable<Base> (IEnumerable(Of Base) en Visual Basic) e imprime los elementos.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 hereda de Base.Derived inherits from Base. En el ejemplo, se crea un tipo List<Derived> (List(Of Derived) en Visual Basic) vacío y se muestra que este tipo se puede pasar a PrintBases y asignar a una variable de tipo IEnumerable<Base> sin conversión alguna.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> implementa IEnumerable<T>, que tiene un solo parámetro de tipo covariante.List<T> implements IEnumerable<T>, which has a single covariant type parameter. El parámetro de tipo covariante es el motivo por el cual se puede usar una instancia de IEnumerable<Derived> en lugar de 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

Volver al principioBack to top

Interfaces genéricas con parámetros de tipo genérico contravarianteGeneric Interfaces with Contravariant Generic Type Parameters

A partir de .NET Framework 4.NET Framework 4, varias interfaces genéricas tienen parámetros de tipo contravariante; por ejemplo: IComparer<T>, IComparable<T>y 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>. Estas interfaces tienen únicamente parámetros de tipo contravariante, por lo que los parámetros de tipo se utilizan solamente como tipos de parámetro en los miembros de las interfaces.These interfaces have only contravariant type parameters, so the type parameters are used only as parameter types in the members of the interfaces.

En el ejemplo siguiente se muestran los parámetros de tipo contravariante.The following example illustrates contravariant type parameters. En el ejemplo se define clase abstractaMustInherit ( Shape en Visual Basic) con una propiedad Area .The example defines an abstract (MustInherit in Visual Basic) Shape class with an Area property. En el ejemplo también se define una clase ShapeAreaComparer que implementa IComparer<Shape> (IComparer(Of Shape) en Visual Basic).The example also defines a ShapeAreaComparer class that implements IComparer<Shape> (IComparer(Of Shape) in Visual Basic). La implementación del método IComparer<T>.Compare se basa en el valor de la propiedad Area , por lo que ShapeAreaComparer se puede usar para ordenar los objetos Shape por área.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.

La clase Circle hereda Shape e invalida Area.The Circle class inherits Shape and overrides Area. En el ejemplo se crea una colección SortedSet<T> de objetos Circle , usando un constructor que toma IComparer<Circle> (IComparer(Of Circle) en 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). Sin embargo, en lugar de pasar IComparer<Circle>, en el ejemplo se pasa un objeto ShapeAreaComparer , que implementa IComparer<Shape>.However, instead of passing an IComparer<Circle>, the example passes a ShapeAreaComparer object, which implements IComparer<Shape>. En el ejemplo se puede pasar un comparador de un tipo menos derivado (Shape) cuando el código llama a un comparador de un tipo más derivado (Circle), ya que el parámetro de tipo de la interfaz genérica IComparer<T> es contravariante.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.

Cuando se agrega un nuevo objeto Circle a SortedSet<Circle>, se llama al método IComparer<Shape>.Compare (IComparer(Of Shape).Compare en Visual Basic) del objeto ShapeAreaComparer cada vez que el nuevo elemento se compara con un elemento existente.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. El tipo de parámetro del método (Shape) es menos derivado que el tipo que se pasa (Circle), por lo que la llamada tiene seguridad de tipos.The parameter type of the method (Shape) is less derived than the type that is being passed (Circle), so the call is type safe. La contravarianza permite a ShapeAreaComparer ordenar una colección de cualquier tipo único, así como a una colección mixta de tipos, que derivan de 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

Volver al principioBack to top

Delegados genéricos con parámetros de tipo varianteGeneric Delegates with Variant Type Parameters

En .NET Framework 4.NET Framework 4, los delegados genéricos Func , como Func<T,TResult>, tienen tipos de valor devueltos covariante y tipos de parámetro contravariante.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. Los delegados genéricos Action , como Action<T1,T2>, tienen tipos de parámetro contravariante.The Action generic delegates, such as Action<T1,T2>, have contravariant parameter types. Esto significa que los delegados se pueden asignar a variables que tengan tipos de parámetro más derivados y (en el caso de los delegados genéricos Func ) tipos de valor devuelto menos derivados.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.

Nota

El último parámetro de tipo genérico de los delegados genéricos Func especifica el tipo del valor devuelto en la firma de delegado.The last generic type parameter of the Func generic delegates specifies the type of the return value in the delegate signature. Es covariante (palabra claveout ), mientras que los otros parámetros de tipo genérico son contravariante (palabra clavein ).It is covariant (out keyword), whereas the other generic type parameters are contravariant (in keyword).

Esto se ilustra en el código siguiente:The following code illustrates this. En el primer fragmento de código, se definen una clase denominada Base, una clase denominada Derived que hereda de Basey otra clase con un método static (Shared en Visual Basic) denominado 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. El método toma una instancia de Base y devuelve una instancia de Derived.The method takes an instance of Base and returns an instance of Derived. (Si el argumento es una instancia de Derived, MyMethod la devuelve; si el argumento es una instancia de Base, MyMethod devuelve una nueva instancia de Derived.) En Main(), se crea en el ejemplo una instancia de Func<Base, Derived> (Func(Of Base, Derived) en Visual Basic) que representa MyMethod, y la almacena en la variable 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

En el segundo fragmento de código, se muestra que el delegado puede asignarse a una variable de tipo Func<Base, Base> (Func(Of Base, Base) en Visual Basic) ya que el tipo de valor devuelto es covariante.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())

En el tercer fragmento de código, se muestra que el delegado puede asignarse a una variable de tipo Func<Derived, Derived> (Func(Of Derived, Derived) en Visual Basic) ya que el tipo de parámetro es contravariante.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())

En el último fragmento de código, se muestra que el delegado puede asignarse a una variable de tipo Func<Derived, Base> (Func(Of Derived, Base) en Visual Basic), combinando los efectos del tipo de parámetro contravariante y el tipo de valor devuelto covariante.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())

La varianza en delegados genéricos y no genéricosVariance in Generic and Non-Generic Delegates

En el código anterior, la signatura de MyMethod coincide exactamente con la signatura del delegado genérico construido: Func<Base, Derived> (Func(Of Base, Derived) en 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). En el ejemplo, se muestra que este delegado genérico se puede almacenar en variables o en parámetros de método que tengan tipos de parámetro más derivados y tipos de valor devuelto menos derivados, siempre y cuando todos los tipos de delegado se construyan a partir del tipo de delegado genérico 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>.

Este es un aspecto importante.This is an important point. Los efectos de la covarianza y la contravarianza en los parámetros de tipo de los delegados genéricos son similares a los efectos de la covarianza y la contravarianza en el enlace a delegados normal; vea Variance in Delegates (C#) (Varianza en delegados [C#]) y Variance in Delegates (Visual Basic) (Varianza en delegados [Visual Basic]).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 (C#) and Variance in Delegates (Visual Basic)). Sin embargo, la varianza en el enlace a delegados funciona con todos los tipos de delegado, no solo con tipos de delegado genérico que tienen parámetros de tipo variante.However, variance in delegate binding works with all delegate types, not just with generic delegate types that have variant type parameters. Además, la varianza en el enlace a delegados permite enlazar un método a cualquier delegado que tenga tipos de parámetro más restrictivos y un tipo de valor devuelto menos restrictivo, mientras que la asignación de delegados genéricos solo funciona si ambos tipos de delegado se construyen a partir de la misma definición de tipo genérico.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.

En el ejemplo siguiente se muestran los efectos combinados de la varianza en el enlace a delegados y la varianza en los parámetros de tipo genérico.The following example shows the combined effects of variance in delegate binding and variance in generic type parameters. En el ejemplo se define una jerarquía de tipos que incluye tres tipos, de menos derivado (Type1) a más derivado (Type3).The example defines a type hierarchy that includes three types, from least derived (Type1) to most derived (Type3). La varianza en el enlace a delegados normal se usa para enlazar un método con un tipo de parámetro de Type1 y un tipo de valor devuelto de Type3 a un delegado genérico con un tipo de parámetro de Type2 y un tipo de valor devuelto de 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. A continuación, el delegado genérico resultante se asigna a otra variable cuyo tipo de delegado genérico tiene un parámetro de tipo Type3 y un tipo de valor devuelto de Type1, usando la covarianza y contravarianza de parámetros de tipo genérico.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. La segunda asignación requiere que tanto el tipo de variable como el tipo de delegado se construyan a partir de la misma definición de tipo genérico, en este caso 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

Volver al principioBack to top

Definir interfaces y delegados genéricos variantesDefining Variant Generic Interfaces and Delegates

A partir de .NET Framework 4.NET Framework 4, Visual Basic y C# tienen palabras clave que permiten marcar como covariantes o contravariantes los parámetros de tipo genérico de las interfaces y los delegados.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.

Nota

A partir de .NET Framework versión 2.0, Common Language Runtime admite anotaciones de varianza en parámetros de tipo genérico.Starting with the .NET Framework version 2.0, the common language runtime supports variance annotations on generic type parameters. Antes de .NET Framework 4.NET Framework 4, la única manera de definir una clase genérica que tuviera estas anotaciones era usar el lenguaje intermedio de Microsoft (MSIL), ya fuera compilando la clase con Ilasm.exe (IL Assembler) o emitiéndola en un ensamblado dinámico.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.

Un parámetro de tipo covariante se marca con la palabra clave out (palabra clave Out en Visual Basic, + para el Ensamblador de MSIL).A covariant type parameter is marked with the out keyword (Out keyword in Visual Basic, + for the MSIL Assembler). Puede usar un parámetro de tipo covariante como el valor devuelto de un método que pertenece a una interfaz o como el tipo de valor devuelto de un delegado.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. No puede usar un parámetro de tipo covariante como una restricción de tipo genérico para los métodos de interfaz.You cannot use a covariant type parameter as a generic type constraint for interface methods.

Nota

Si un método de una interfaz tiene un parámetro que es un tipo de delegado genérico, se puede usar un parámetro de tipo covariante del tipo de interfaz para especificar un parámetro de tipo contravariante del tipo de delegado.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.

Un parámetro de tipo contravariante se marca con la palabra clave in (palabra claveIn en Visual Basic, - para el Ensamblador de MSIL).A contravariant type parameter is marked with the in keyword (In keyword in Visual Basic, - for the MSIL Assembler). Puede usar un parámetro de tipo contravariante como el tipo de un parámetro de un método que pertenece a una interfaz o como el tipo de un parámetro de un delegado.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. Puede usar un parámetro de tipo contravariante como una restricción de tipo genérico para un método de interfaz.You can use a contravariant type parameter as a generic type constraint for an interface method.

Solo los tipos de interfaz y los tipos de delegado pueden tener parámetros de tipo variante.Only interface types and delegate types can have variant type parameters. Un tipo de interfaz o un tipo de delegado puede tener parámetros de tipo covariante y contravariante.An interface or delegate type can have both covariant and contravariant type parameters.

Visual Basic y C# no le permiten infringir las reglas de uso de parámetros de tipo covariante y contravariante ni agregar anotaciones de covarianza y contravarianza a los parámetros de tipo de tipos distintos de interfaces y delegados.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. El Ensamblador de MSIL no realiza esas comprobaciones, pero se produce TypeLoadException si intenta cargar un tipo que infringe las reglas.The MSIL Assembler does not perform such checks, but a TypeLoadException is thrown if you try to load a type that violates the rules.

Para obtener información y código de ejemplo, vea Varianza en interfaces genéricas (C#) y Varianza en interfaces genéricas (Visual Basic).For information and example code, see Variance in Generic Interfaces (C#) and Variance in Generic Interfaces (Visual Basic).

Volver al principioBack to top

Lista de tipos de interfaces y delegados genéricos variantesList of Variant Generic Interface and Delegate Types

En .NET Framework 4.NET Framework 4, los siguientes tipos de interfaz y delegado tienen parámetros de tipo covariante y/o contravariante.In the .NET Framework 4.NET Framework 4, the following interface and delegate types have covariant and/or contravariant type parameters.

TipoType Parámetros de tipo covarianteCovariant type parameters Parámetros de tipo contravarianteContravariant type parameters
Action<T> a 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> a 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

Vea tambiénSee also