Covarianza e controvarianza nei genericsCovariance and Contravariance in Generics

La covarianza e la controvarianza sono termini che fanno riferimento alla possibilità di usare un tipo più derivato (più specifico) o un tipo meno derivato (meno specifico) di quanto specificato in origine.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. I parametri di tipo generico supportano la covarianza e la controvarianza per offrire la massima flessibilità nell'assegnazione e nell'utilizzo dei tipi generici.Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. Quando si fa riferimento a un sistema di tipi, la covarianza, la controvarianza e l'invarianza hanno le seguenti definizioni.When you are referring to a type system, covariance, contravariance, and invariance have the following definitions. Negli esempi si presuppone una classe di base denominata Base e una classe derivata denominata Derived.The examples assume a base class named Base and a derived class named Derived.

  • Covariance

    Permette di usare un tipo più derivato di quello originariamente specificato.Enables you to use a more derived type than originally specified.

    È possibile assegnare un'istanza di IEnumerable<Derived> (IEnumerable(Of Derived) in Visual Basic) a una variabile di 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

    Consente di utilizzare un tipo più generico (meno derivato) di quello originariamente specificato.Enables you to use a more generic (less derived) type than originally specified.

    È possibile assegnare un'istanza di IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) a una variabile di tipo IEnumerable<Derived>.You can assign an instance of IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) to a variable of type IEnumerable<Derived>.

  • Invariance

    Significa che è possibile usare solo il tipo specificato originariamente; pertanto un parametro di tipo generico invariante non è covariante o controvariante.Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

    Non è possibile assegnare un'istanza di IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) a una variabile di tipo IEnumerable<Derived> o viceversa.You cannot assign an instance of IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) to a variable of type IEnumerable<Derived> or vice versa.

I parametri di tipo covariante consentono di effettuare assegnazioni simili al polimorfismo ordinario., come illustrato nel codice seguente.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 classe List<T> implementa l'interfaccia IEnumerable<T> , pertanto List<Derived> (List(Of Derived) in 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>. Il parametro di tipo covariante completa l'operazione.The covariant type parameter does the rest.

Viceversa, la controvarianza è poco intuitiva.Contravariance, on the other hand, seems counterintuitive. Nell'esempio seguente viene creato un delegato di tipo Action<Base> (Action(Of Base) in Visual Basic), che viene quindi assegnato a una variabile di 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())

Può sembrare un passo indietro, ma si tratta di codice indipendente dai tipi che viene compilato ed eseguito.This seems backward, but it is type-safe code that compiles and runs. L'espressione lambda corrisponde al delegato a cui è assegnato, pertanto definisce un metodo che accetta un parametro di tipo Base senza valore restituito.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. Il delegato risultante può essere assegnato a una variabile di tipo Action<Derived> poiché il parametro di tipo T del delegato Action<T> è controvariante.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. Il codice è indipendente dai tipi perché T specifica un tipo di parametro.The code is type-safe because T specifies a parameter type. Quando il delegato di tipo Action<Base> viene richiamato come se fosse un delegato di tipo Action<Derived>, il relativo argomento deve essere di 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. Questo argomento può sempre essere passato in modo sicuro al metodo sottostante, perché il parametro del metodo è di tipo Base.This argument can always be passed safely to the underlying method, because the method's parameter is of type Base.

In genere, un parametro di tipo covariante può essere utilizzato come tipo restituito di un delegato e i parametri di tipo controvariante possono essere utilizzati come tipi di parametri.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. Per un'interfaccia, i parametri di tipo covariante possono essere utilizzati come tipi restituiti dei metodi dell'interfaccia e i parametri di tipo controvariante possono essere utilizzati come tipi di parametri dei metodi dell'interfaccia.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 e la controvarianza sono definite collettivamente varianza.Covariance and contravariance are collectively referred to as variance. Un parametro di tipo generico non contrassegnato come covariante o controvariante viene definito invariante.A generic type parameter that is not marked covariant or contravariant is referred to as invariant. Di seguito vengono riepilogati i concetti relativi alla varianza in Common Language Runtime:A brief summary of facts about variance in the common language runtime:

  • In .NET Framework 4.NET Framework 4, i parametri di tipo variante sono limitati ai tipi di interfaccia generica e delegato generico.In the .NET Framework 4.NET Framework 4, variant type parameters are restricted to generic interface and generic delegate types.

  • Un tipo di interfaccia generica o delegato generico può presentare parametri di tipo sia covariante sia controvariante.A generic interface or generic delegate type can have both covariant and contravariant type parameters.

  • La varianza si applica solo ai tipi di riferimento. Se si specifica un tipo di valore per un parametro di tipo variante, tale parametro di tipo è invariante per il tipo costruito risultante.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 non si applica alla combinazione di delegati.Variance does not apply to delegate combination. Ciò significa che nel caso di due delegati di tipo Action<Derived> e Action<Base> (Action(Of Derived) e Action(Of Base) in Visual Basic), non è possibile combinare il secondo delegato con il primo anche se il risultato sarebbe indipendente dai tipi.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 consente l'assegnazione del secondo delegato a una variabile di tipo Action<Derived>, ma i delegati possono essere combinati solo se il loro tipo corrisponde esattamente.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.

Nelle sottosezioni seguenti vengono descritti in dettaglio parametri di tipo covariante e controvariante:The following subsections describe covariant and contravariant type parameters in detail:

Interfacce generiche con parametri di tipo covarianteGeneric Interfaces with Covariant Type Parameters

A partire da .NET Framework 4.NET Framework 4, diverse interfacce generiche presentano parametri di tipo controvariante. Ad esempio: IEnumerable<T>, IEnumerator<T>, IQueryable<T>e 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>. Tutti i parametri del tipo di queste interfacce sono covarianti, pertanto i parametri del tipo vengono utilizzati solo per i tipi restituiti dei membri.All the type parameters of these interfaces are covariant, so the type parameters are used only for the return types of the members.

Nell'esempio seguente vengono illustrati parametri di tipo covariante.The following example illustrates covariant type parameters. Nell'esempio vengono definiti due tipi: Base presenta un metodo statico denominato PrintBases che accetta IEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) e stampa gli elementi.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 eredita da Base.Derived inherits from Base. Nell'esempio viene creato un tipo List<Derived> (List(Of Derived) in Visual Basic) vuoto e viene illustrato che è possibile passare tale tipo a PrintBases e assegnarlo a una variabile di tipo IEnumerable<Base> senza eseguire il cast.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>, che dispone di un solo parametro di tipo covariante.List<T> implements IEnumerable<T>, which has a single covariant type parameter. Il parametro di tipo covariante è il motivo per cui è possibile usare un'istanza di IEnumerable<Derived> anziché di 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

Torna all'inizioBack to top

Interfacce generiche con parametri di tipo generico controvarianteGeneric Interfaces with Contravariant Generic Type Parameters

A partire da .NET Framework 4.NET Framework 4, diverse interfacce generiche presentano parametri di tipo controvariante. Ad esempio: IComparer<T>, IComparable<T>e 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>. Queste interfacce dispongono solo di parametri di tipo controvariante, pertanto i parametri di tipo vengono utilizzati solo come tipi di parametri nei membri delle interfacce.These interfaces have only contravariant type parameters, so the type parameters are used only as parameter types in the members of the interfaces.

Nell'esempio seguente vengono illustrati parametri di tipo controvariante.The following example illustrates contravariant type parameters. Nell'esempio viene definita una classeMustInherit astratta ( Shape in Visual Basic) con una proprietà Area .The example defines an abstract (MustInherit in Visual Basic) Shape class with an Area property. Nell'esempio viene definita anche una classe ShapeAreaComparer che implementa IComparer<Shape> (IComparer(Of Shape) in Visual Basic).The example also defines a ShapeAreaComparer class that implements IComparer<Shape> (IComparer(Of Shape) in Visual Basic). L'implementazione del metodo IComparer<T>.Compare è basata sul valore della proprietà Area, pertanto ShapeAreaComparer può essere utilizzato per ordinare gli oggetti Shape in base all'area.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 classe Circle eredita dalla classe Shape ed esegue l'override di Area.The Circle class inherits Shape and overrides Area. Nell'esempio viene creato un oggetto SortedSet<T> di oggetti Circle , usando un costruttore che accetta un oggetto IComparer<Circle> (IComparer(Of Circle) in 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). Tuttavia, anziché passare un oggetto IComparer<Circle>, nell'esempio viene passato un oggetto ShapeAreaComparer che implementa IComparer<Shape>.However, instead of passing an IComparer<Circle>, the example passes a ShapeAreaComparer object, which implements IComparer<Shape>. È possibile passare un operatore di confronto di un tipo meno derivato (Shape) quando il codice chiama un operatore di confronto di un tipo più derivato (Circle), poiché il parametro di tipo dell'interfaccia generica IComparer<T> è controvariante.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.

Quando un nuovo oggetto Circle viene aggiunto all'oggetto SortedSet<Circle>, il metodo IComparer<Shape>.Compare (metodoIComparer(Of Shape).Compare in Visual Basic) dell'oggetto ShapeAreaComparer viene chiamato ogni volta che il nuovo elemento viene confrontato con un elemento esistente.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. Il tipo di parametro del metodo (Shape) è meno derivato rispetto al tipo passato (Circle), pertanto la chiamata è indipendente dai tipi.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 controvarianza consente a ShapeAreaComparer di ordinare una raccolta di qualsiasi singolo tipo nonché una raccolta mista di tipi che derivano da 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

Torna all'inizioBack to top

Delegati generici con parametri di tipo varianteGeneric Delegates with Variant Type Parameters

In .NET Framework 4.NET Framework 4i delegati generici Func , ad esempio Func<T,TResult>, presentano tipi restituiti covarianti e tipi di parametro controvarianti.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. I delegati generici Action , ad esempio Action<T1,T2>, presentano tipi di parametro controvarianti.The Action generic delegates, such as Action<T1,T2>, have contravariant parameter types. Ciò significa che i delegati possono essere assegnati a variabili che presentano tipi di parametro più derivati e (nel caso dei delegati generici Func) tipi restituiti meno derivati.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

L'ultimo parametro di tipo generico dei delegati generici Func specifica il tipo del valore restituito nella firma del delegato.The last generic type parameter of the Func generic delegates specifies the type of the return value in the delegate signature. È covariante (parola chiaveout ), mentre gli altri parametri di tipo generico sono controvarianti (parola chiavein ).It is covariant (out keyword), whereas the other generic type parameters are contravariant (in keyword).

Questa condizione è illustrata nel codice che segue.The following code illustrates this. Nella prima parte di codice vengono definite una classe denominata Base, una classe denominata Derived che eredita da Basee un'altra classe con un metodo static (Shared in Visual Basic) denominata 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. Il metodo accetta un'istanza di Base e restituisce un'istanza di Derived.The method takes an instance of Base and returns an instance of Derived. Se l'argomento è un'istanza di Derived, MyMethod la restituisce. Se l'argomento è un'istanza di Base, MyMethod restituisce una nuova istanza di Derived. In Main(), nell'esempio viene creata un'istanza di Func<Base, Derived> (Func(Of Base, Derived) in Visual Basic) che rappresenta MyMethod, quindi l'istanza viene archiviata nella variabile 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

Nella seconda parte di codice viene illustrato che è possibile assegnare il delegato a una variabile di tipo Func<Base, Base> (Func(Of Base, Base) in Visual Basic), poiché il tipo restituito è 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())

Nella terza parte di codice viene illustrato che è possibile assegnare il delegato a una variabile di tipo Func<Derived, Derived> (Func(Of Derived, Derived) in Visual Basic), poiché il tipo di parametro è controvariante.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())

Nella parte finale di codice viene illustrato che è possibile assegnare il delegato a una variabile di tipo Func<Derived, Base> (Func(Of Derived, Base) in Visual Basic), combinando gli effetti del tipo di parametro controvariante e del tipo restituito 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())

Varianza nei delegati generici e non genericiVariance in Generic and Non-Generic Delegates

Nel codice precedente la firma di MyMethod corrisponde esattamente alla firma del delegato generico costruito: Func<Base, Derived> (Func(Of Base, Derived) in 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). Nell'esempio viene illustrato che è possibile archiviare questo delegato generico in variabili o parametri del metodo che presentano tipi di parametro più derivati e tipi restituiti meno derivati, purché tutti i tipi delegati vengano costruiti a partire dal tipo delegato generico 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>.

Questo è un punto importante.This is an important point. Gli effetti della covarianza e della controvarianza nei parametri di tipo dei delegati generici sono simili agli effetti della covarianza e della controvarianza nel normale binding di delegati (vedere Varianza nei delegati).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). Tuttavia, la varianza nell'associazione di delegati funziona con tutti i tipi delegati, non solo con quelli generici aventi parametri di tipo variante.However, variance in delegate binding works with all delegate types, not just with generic delegate types that have variant type parameters. Inoltre, la varianza nell'associazione di delegati consente l'associazione di un metodo a qualsiasi delegato con tipi di parametro più restrittivi e un tipo restituito meno restrittivo, mentre l'assegnazione di delegati generici funziona solo se entrambi i tipi di delegati sono costruiti a partire dalla stessa definizione di tipo generico.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.

Nell'esempio seguente sono mostrati gli effetti combinati della varianza nell'associazione di delegati e della varianza nei parametri di tipo generico.The following example shows the combined effects of variance in delegate binding and variance in generic type parameters. Nell'esempio viene definita una gerarchia di tre tipi, da quello meno derivato (Type1) a quello più derivato (Type3).The example defines a type hierarchy that includes three types, from least derived (Type1) to most derived (Type3). La varianza nella normale associazione di delegati viene utilizzata per associare un metodo con un tipo di parametro Type1 e un tipo restituito Type3 a un delegato generico con un tipo di parametro Type2 e un tipo restituito 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. Il delegato generico risultante viene quindi assegnato a un'altra variabile il cui tipo delegato generico presenta un parametro di tipo Type3 e un tipo restituito Type1, utilizzando la covarianza e la controvarianza di parametri di tipo generico.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. Per la seconda assegnazione è necessario che sia il tipo della variabile sia quello del delegato vengano costruiti a partire dalla stessa definizione di tipo generico, in questo 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

Torna all'inizioBack to top

Definizione di interfacce e delegati generici variantiDefining Variant Generic Interfaces and Delegates

A partire da .NET Framework 4.NET Framework 4, Visual Basic e C# presentano parole chiave che consentono di contrassegnare i parametri di tipo generico di interfacce e delegati come covarianti o controvarianti.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 partire dalla versione 2.0 di .NET Framework, Common Language Runtime supporta le annotazioni di variante nei parametri di tipo generico.Starting with the .NET Framework version 2.0, the common language runtime supports variance annotations on generic type parameters. Nelle versioni precedenti a .NET Framework 4.NET Framework 4, l'unico modo per definire una classe generica con queste annotazioni consiste nell'usare Microsoft Intermediate Language (MSIL), compilando la classe con Ilasm.exe (assembler IL) oppure generandola in un assembly dinamico.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 parametro di tipo covariante è contrassegnato con la parola chiave out (parola chiave Out in Visual Basic, + per l'assembler MSIL).A covariant type parameter is marked with the out keyword (Out keyword in Visual Basic, + for the MSIL Assembler). È possibile usare un parametro di tipo covariante come valore restituito di un metodo che appartiene a un'interfaccia o come tipo restituito di un delegato.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. Non è possibile usare un parametro di tipo covariante come vincolo di tipo generico per i metodi di interfaccia.You cannot use a covariant type parameter as a generic type constraint for interface methods.

Nota

Se un metodo di un'interfaccia presenta un parametro che è un tipo delegato generico, per specificare un parametro di tipo controvariante del tipo delegato è possibile usare un parametro di tipo covariante del tipo di interfaccia.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 parametro di tipo controvariante è contrassegnato con la parola chiave in (parola chiaveIn in Visual Basic, - per l' assembler MSIL).A contravariant type parameter is marked with the in keyword (In keyword in Visual Basic, - for the MSIL Assembler). È possibile usare un parametro di tipo controvariante come tipo di un parametro di un metodo che appartiene a un'interfaccia o come tipo di un parametro di un delegato.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. È possibile usare un parametro di tipo controvariante come vincolo di tipo generico per un metodo di interfaccia.You can use a contravariant type parameter as a generic type constraint for an interface method.

Solo i tipi di interfaccia e i tipi delegati possono presentare parametri di tipo variante.Only interface types and delegate types can have variant type parameters. Un tipo di interfaccia o delegato può presentare parametri di tipo sia covariante sia controvariante.An interface or delegate type can have both covariant and contravariant type parameters.

Visual Basic e C# non consentono di violare le regole per l'utilizzo di parametri di tipo covariante e controvariante o aggiungere annotazioni di covarianza e controvarianza ai parametri relativi a tipi diversi da interfacce e delegati.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. L' assembler MSIL non esegue tali controlli. Tuttavia, se si tenta di caricare un tipo che viola le regole, viene generato un oggetto 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.

Per informazioni e codici di esempio, vedere Varianza nelle interfacce generiche.For information and example code, see Variance in Generic Interfaces.

Torna all'inizioBack to top

Elenco di tipi di interfacce e delegati generici variantiList of Variant Generic Interface and Delegate Types

In .NET Framework 4.NET Framework 4, il tipo di interfaccia e il tipo delegato seguenti presentano parametri di tipo covariante e/o controvariante.In the .NET Framework 4.NET Framework 4, the following interface and delegate types have covariant and/or contravariant type parameters.

TipoType Parametri di tipo covarianteCovariant type parameters Parametri di tipo controvarianteContravariant type parameters
Action<T> in 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> YesYes
Comparison<T> YesYes
Converter<TInput,TOutput> YesYes YesYes
Func<TResult> YesYes
Func<T,TResult> in 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> YesYes YesYes
IComparable<T> YesYes
Predicate<T> YesYes
IComparer<T> YesYes
IEnumerable<T> YesYes
IEnumerator<T> YesYes
IEqualityComparer<T> YesYes
IGrouping<TKey,TElement> YesYes
IOrderedEnumerable<TElement> YesYes
IOrderedQueryable<T> YesYes
IQueryable<T> YesYes

Vedere ancheSee Also

Covarianza e controvarianza (C#)Covariance and Contravariance (C#)
Covarianza e controvarianza (Visual Basic) Covariance and Contravariance (Visual Basic)
Varianza nei delegatiVariance in Delegates