Compartilhar via


Criando Interfaces genéricas de variante (C# e Visual Basic)

Você pode declarar parâmetros de tipo genérico em interfaces como covariant ou contravariant. Covariância permite que os métodos da interface tenham mais retorno tipos derivados do que o definido pelo parâmetro de tipo genérico. / Contravariância permite que os métodos de interface para que os tipos de argumento são derivados menos do que o especificado pelos parâmetros genéricos. Uma interface genérica que tem covariant ou contravariant parâmetros de tipo genérico é chamado de variante.

ObservaçãoObservação

.NET Framework 4 introduz o suporte de variação para várias interfaces de genéricos existentes. Para obter a lista das interfaces variant na.NET Framework, consulte Variação em Interfaces genéricas (C# e Visual Basic).

Declarando Interfaces genéricas da variante

Você pode declarar variant interfaces genéricas, usando o in e out palavras-chave para parâmetros de tipo genérico.

Observação importanteImportante

ByRefparâmetros de Visual Basic e ref e out parâmetros em C# não podem ser variant. Tipos de valor também não suportam a variação.

Você pode declarar um parâmetro de tipo genérico covariant usando o out palavra-chave. O tipo covariant deve atender aos seguintes condições:

  • O tipo é usado apenas como um tipo de retorno dos métodos de interface e não é usado como um tipo de argumentos do método. Isso é ilustrado no exemplo a seguir, na qual o tipo de R é declarado covariant.

    Interface ICovariant(Of Out R)
        Function GetSomething() As R
        ' The following statement generates a compiler error.
        ' Sub SetSomething(ByVal sampleArg As R)
    End Interface
    
    interface ICovariant<out R>
    {
        R GetSomething();
        // The following statement generates a compiler error.
        // void SetSometing(R sampleArg);
    
    }
    

    Há uma exceção a essa regra. Se você tiver um delegado genérico do contravariant como um parâmetro de método, você pode usar o tipo como um parâmetro de tipo genérico para o delegado. Isso é ilustrado pelo tipo de R o exemplo a seguir. Para obter mais informações, consulte Variação de delegados (C# e Visual Basic) e Usando a variação para representantes genéricos Func e ação (C# e Visual Basic).

    Interface ICovariant(Of Out R)
        Sub DoSomething(ByVal callback As Action(Of R))
    End Interface
    
    interface ICovariant<out R>
    {
        void DoSomething(Action<R> callback);
    }
    
  • O tipo não é usado como restrição genérica para os métodos de interface. Isso é ilustrado no código a seguir.

    Interface ICovariant(Of Out R)
        ' The following statement generates a compiler error
        ' because you can use only contravariant or invariant types
        ' in generic contstraints.
        ' Sub DoSomething(Of T As R)()
    End Interface
    
    interface ICovariant<out R>
    {
        // The following statement generates a compiler error
        // because you can use only contravariant or invariant types
        // in generic contstraints.
        // void DoSomething<T>() where T : R;
    }
    

Você pode declarar um contravariant de parâmetro de tipo genérico, usando o in palavra-chave. O tipo contravariant pode ser usado apenas como um tipo de argumentos do método e não como um tipo de retorno dos métodos de interface. O tipo contravariant também pode ser usado para restrições genéricas. O código a seguir mostra como declarar uma interface contravariant e usar uma restrição genérica para um de seus métodos.

Interface IContravariant(Of In A)
    Sub SetSomething(ByVal sampleArg As A)
    Sub DoSomething(Of T As A)()
    ' The following statement generates a compiler error.
    ' Function GetSomething() As A
End Interface
interface IContravariant<in A>
{
    void SetSomething(A sampleArg);
    void DoSomething<T>() where T : A;
    // The following statement generates a compiler error.
    // A GetSomething();            
}

Também é possível oferecer suporte tanto o covariância/contravariância na mesma interface, mas para os parâmetros de tipo diferente, como mostrado no exemplo de código a seguir.

Interface IVariant(Of Out R, In A)
    Function GetSomething() As R
    Sub SetSomething(ByVal sampleArg As A)
    Function GetSetSomething(ByVal sampleArg As A) As R
End Interface
interface IVariant<out R, in A>
{
    R GetSomething();
    void SetSomething(A sampleArg);
    R GetSetSometings(A sampleArg);
}

No Visual Basic, é possível declarar eventos em interfaces variant sem especificar o tipo de delegado. Além disso, uma interface variant não pode ter aninhados, classes, enumerações ou estruturas, mas pode ter aninhados interfaces. Isso é ilustrado no código a seguir.

Interface ICovariant(Of Out R)
    ' The following statement generates a compiler error.
    ' Event SampleEvent()
    ' The following statement specifies the delegate type and 
    ' does not generate an error.
    Event AnotherEvent As EventHandler

    ' The following statements generate compiler errors,
    ' because a variant interface cannot have
    ' nested enums, classes, or structures.

    'Enum SampleEnum : test : End Enum
    'Class SampleClass : End Class
    'Structure SampleStructure : Dim value As Integer : End Structure

    ' Variant interfaces can have nested interfaces.
    Interface INested : End Interface
End Interface

A implementação de Interfaces genéricas da variante

Você pode implementar variant interfaces genéricas em classes usando a mesma sintaxe que é usada para interfaces invariável. O exemplo de código a seguir mostra como implementar uma interface covariant em uma classe genérica.

Interface ICovariant(Of Out R)
    Function GetSomething() As R
End Interface

Class SampleImplementation(Of R)
    Implements ICovariant(Of R)
    Public Function GetSomething() As R _
    Implements ICovariant(Of R).GetSomething
        ' Some code.
    End Function
End Class
interface ICovariant<out R>
{
    R GetSomething();
}
class SampleImplementation<R> : ICovariant<R>
{
    public R GetSomething()
    {
        // Some code.
        return default(R);
    }
}

Classes que implementam interfaces variant são invariável. Por exemplo, considere o seguinte código.

' The interface is covariant.
Dim ibutton As ICovariant(Of Button) =
    New SampleImplementation(Of Button)
Dim iobj As ICovariant(Of Object) = ibutton

' The class is invariant.
Dim button As SampleImplementation(Of Button) =
    New SampleImplementation(Of Button)
' The following statement generates a compiler error
' because classes are invariant.
' Dim obj As SampleImplementation(Of Object) = button
// The interface is covariant.
ICovariant<Button> ibutton = new SampleImplementation<Button>();
ICovariant<Object> iobj = ibutton;

// The class is invariant.
SampleImplementation<Button> button = new SampleImplementation<Button>();
// The following statement generates a compiler error
// because classes are invariant.
// SampleImplementation<Object> obj = button;

Estender Interfaces genéricas da variante

Quando você estende uma interface genérica variant, você precisa usar o in e out palavras-chave para especificar explicitamente se oferece suporte à interface derivada variação. O compilador não inferir a variação da interface que está sendo estendida. Por exemplo, considere as seguintes interfaces.

Interface ICovariant(Of Out T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T)
End Interface

Interface IExtCovariant(Of Out T)
    Inherits ICovariant(Of T)
End Interface
interface ICovariant<out T> { }
interface IInvariant<T> : ICovariant<T> { }
interface IExtCovariant<out T> : ICovariant<T> { }

No IInvariant<T> (Invariant(Of T) em Visual Basic) de interface, o parâmetro de tipo genérico T é invariável, enquanto em IExtCovariant<out T> (IExtCovariant (Of Out T) em Visual Basic) o parâmetro de tipo é covariant, embora ambas as interfaces de estender a mesma interface. A mesma regra será aplicada aos parâmetros de tipo genérico contravariant.

Você pode criar uma interface que se estende a interface onde o parâmetro de tipo genérico T é covariant e a interface onde ele consta contravariant se estendendo o parâmetro de tipo genérico de interface T é constante. This is illustrated in the following code example.

Interface ICovariant(Of Out T)
End Interface

Interface IContravariant(Of In T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T), IContravariant(Of T)
End Interface
interface ICovariant<out T> { }
interface IContravariant<in T> { }
interface IInvariant<T> : ICovariant<T>, IContravariant<T> { }

No entanto, se um parâmetro de tipo genérico T é covariant declarado em uma interface, você não pode declará-lo contravariant na interface de extensão, ou vice versa. This is illustrated in the following code example.

Interface ICovariant(Of Out T)
End Interface

' The following statements generate a compiler error.
' Interface ICoContraVariant(Of In T)
'     Inherits ICovariant(Of T)
' End Interface
interface ICovariant<out T> { }
// The following statement generates a compiler error.
// interface ICoContraVariant<in T> : ICovariant<T> { }

Evitando ambigüidade

Quando você implementa variant interfaces genéricas, variação, às vezes, pode levar à ambigüidade. Isso deve ser evitado.

Por exemplo, se você implementar explicitamente a mesma interface genérica variant com parâmetros de tipo genérico diferente em uma classe, ele pode criar a ambigüidade. O compilador produz um erro neste caso, mas não for especificada a implementação de interface que será escolhida em tempo de execução. Isso pode levar a erros sutis no seu código. Considere o exemplo de código a seguir.

ObservaçãoObservação

Com Option Strict Off, Visual Basic gera um aviso do compilador quando há uma implementação de interface ambíguo. Com Option Strict On, Visual Basic gera um erro do compilador.

' Simple class hierarchy.
Class Animal
End Class

Class Cat
    Inherits Animal
End Class

Class Dog
    Inherits Animal
End Class

' This class introduces ambiguity
' because IEnumerable(Of Out T) is covariant.
Class Pets
    Implements IEnumerable(Of Cat), IEnumerable(Of Dog)

    Public Function GetEnumerator() As IEnumerator(Of Cat) _
        Implements IEnumerable(Of Cat).GetEnumerator
        Console.WriteLine("Cat")
        ' Some code.
    End Function

    Public Function GetEnumerator1() As IEnumerator(Of Dog) _
        Implements IEnumerable(Of Dog).GetEnumerator
        Console.WriteLine("Dog")
        ' Some code.
    End Function

    Public Function GetEnumerator2() As IEnumerator _
        Implements IEnumerable.GetEnumerator
        ' Some code.
    End Function
End Class

Sub Main()
    Dim pets As IEnumerable(Of Animal) = New Pets()
    pets.GetEnumerator()
End Sub
// Simple class hierarchy.
class Animal { }
class Cat : Animal { }
class Dog : Animal { }

// This class introduces ambiguity
// because IEnumerable<out T> is covariant.
class Pets : IEnumerable<Cat>, IEnumerable<Dog>
{
    IEnumerator<Cat> IEnumerable<Cat>.GetEnumerator()
    {
        Console.WriteLine("Cat");
        // Some code.
        return null;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        // Some code.
        return null;
    }

    IEnumerator<Dog> IEnumerable<Dog>.GetEnumerator()
    {
        Console.WriteLine("Dog");
        // Some code.
        return null;
    }
}
class Program
{
    public static void Test()
    {
        IEnumerable<Animal> pets = new Pets();
        pets.GetEnumerator();
    }
}

Neste exemplo, ele não for especificado como o pets.GetEnumerator método escolhe entre Cat e Dog. Isso poderia causar problemas no seu código.

Consulte também

Referência

Usando a variação para representantes genéricos Func e ação (C# e Visual Basic)

Conceitos

Variação em Interfaces genéricas (C# e Visual Basic)