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ção |
---|
.NET Framework 4 introduz o suporte de variação para várias interfaces genéricas 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 os parâmetros de tipo genérico.
Importante |
---|
ByRefparâmetros no Visual Basic e ref e out parâmetros em C# não podem ser uma variante.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 é mostrado 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 no exemplo a seguir.Para obter mais informações, consulte Variação em 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 utilizar uma restrição genérica para um dos 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, conforme 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, você não pode declarar eventos em interfaces variant sem especificar o tipo delegate.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 interfaces genéricas variant nas 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 interface derivada oferece suporte a 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 estendem 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 é contravariant em caso da estender o parâmetro de tipo genérico de interface T é invariável.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 é declarado covariant em uma única 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 interfaces genéricas variant, 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 diferentes em uma classe, ele pode criar a ambigüidade.O compilador não produz um erro neste caso, mas ele não for especificado será escolhida qual implementação de interface em tempo de execução.Isso pode levar a erros sutis no seu código.Considere o exemplo de código a seguir.
Observaçã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)