Tworzenie interfejsów typu Variant (C# and Visual Basic)

Można zadeklarować parametr typu rodzajowego w interfejsach jako kowariantnego lub kontrawariantnego.Kowariancja umożliwia metody interfejsu bardziej pochodnym zwracanych typów niż określone przez parametr typu rodzajowego.Kontrawariancja umożliwia metody interfejsu mają typy argumentów, które są mniej pochodny, niż określona przez parametry rodzajowe.Rodzajowy interfejs, który ma kowariantnego lub kontrawariantnego parametry typu rodzajowego jest nazywany Wariant.

[!UWAGA]

.NET Framework 4 wprowadzono obsługę wariancji dla kilku istniejących interfejsów rodzajowy.Dla listy wariantu interfejsów w.NET Framework, zobacz Wariancje w interfejsach (C# i Visual Basic).

Deklarowanie interfejsów rodzajowy wariant

Wariant rodzajowy interfejsów można zadeklarować za pomocą in i out słowa kluczowe parametry typu rodzajowego.

Ważna uwagaWażne

ByRefParametry w języku Visual Basic i ref i out parametrów w języku C# nie może być wariantem.Typów wartości nie obsługują również wariancji.

Można zadeklarować parametr typu rodzajowego kowariantnego za pomocą out słowa kluczowego.Typu kowariantnego muszą spełniać następujące warunki:

  • Typ jest używany tylko jako zwracany typ metody interfejsu i nie są używane jako typ argumenty tej metody.Jest to zademonstrowane w poniższym przykładzie, w którym typu R jest zadeklarowany jako kowariantnego.

    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);
    
    }
    

    Istnieje jeden wyjątek od tej zasady.Jeśli masz kontrawariantnego Delegat rodzajowy jako parametr metody, można użyć typu jako parametr typu rodzajowego do pełnomocnika.Jest to zilustrowane przez typ R w następującym przykładzie.Aby uzyskać więcej informacji, zobacz Wariancje w delegatach (C# i Visual Basic) i Korzystanie z wariancji dla delegatów Func i Action (C# i 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);
    }
    
  • Typ nie jest używany jako rodzajowy ograniczenia dla metod interfejsu.Ilustruje to poniższy kod.

    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;
    }
    

Można zadeklarować kontrawariantnego parametr typu rodzajowego przy użyciu in słowa kluczowego.Typ kontrawariantnego mogą służyć jedynie jako typ argumenty tej metody, a nie zwracany typ metody interfejsu.Można również kontrawariantnego typ rodzajowy ograniczeń.Poniższy kod pokazuje, jak zadeklarować interfejsu kontrawariantnego i używania rodzajowy ograniczenie dla jednego z jego metody.

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();            
}

Jest również możliwe obsługuje zarówno Kowariancja i kontrawariancja, w tym samym interfejsu, ale dla parametrów innego typu, jak pokazano w poniższym przykładzie kodu.

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);
}

W języku Visual Basic nie można zadeklarować zdarzenia w interfejsach typu variant nie określając typ obiektu delegowanego.Ponadto interfejs wariantu nie można posiadać zagnieżdżonych klas, teksty stałe lub struktur, ale mogą być zagnieżdżone interfejsów.Ilustruje to poniższy kod.

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

Implementowanie interfejsów rodzajowy wariant

Zaimplementowaniem wariantu rodzajowy interfejsy klas przy użyciu składni, która jest używana dla interfejsów niezmienne.Poniższy przykład kodu pokazuje, jak zaimplementować interfejsu kowariantnego w klasie rodzajowej.

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);
    }
}

Klasy, które implementują interfejsy typu variant są niezmienne.Na przykład rozważmy następujący kod.

' 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;

Rozszerzanie interfejsów rodzajowy wariant

Podczas rozszerzania wariantu interfejs generic trzeba używać in i out słowa kluczowe, aby jawnie określić, czy interfejsu pochodnego obsługuje wariancji.Odchylenie od interfejs, który zostanie rozszerzone są rozpoznawane przez kompilator.Na przykład rozważmy następujące interfejsy.

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> { }

W IInvariant<T> (Invariant(Of T) w języku Visual Basic) interfejsu parametr typu rodzajowego T jest niezmienny, należy w IExtCovariant<out T> (IExtCovariant (Of Out T) w języku Visual Basic) parametr typu jest kowariantnego, chociaż oba interfejsy rozszerzenie tego samego interfejsu.Tym samym reguła jest stosowana do kontrawariantnego parametr typu rodzajowego.

Można utworzyć interfejs, który rozszerza interfejs gdzie parametr typu rodzajowego T jest kowariantnego i interfejsu, gdzie jest kontrawariantnego w razie rozszerzenia interfejsu parametr typu rodzajowego T jest niezmienny.Ilustruje to poniższy przykład kodu.

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> { }

Jednakże jeśli parametr typu rodzajowego T jest nie kowariantnego zadeklarowane w jeden interfejs, można zadeklarować ją kontrawariantnego rozszerzającego interfejsu, lub odwrotnie.Ilustruje to poniższy przykład kodu.

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> { }

Uniknięcia niejednoznaczności

Podczas implementowania wariantu interfejsów rodzajowy odchylenie czasami może prowadzić do niejednoznaczności.Powinno się unikać.

Na przykład jeśli zostanie jawnie zaimplementowane wariantu tego samego interfejsu rodzajowe z parametrami innego typu rodzajowego w jednej klasie, można utworzyć niejednoznaczności.Kompilator nie przedstawił w tym przypadku błąd, ale nie jest określony, implementacji interfejsu, który zostanie wybrany w czasie wykonywania.Może to prowadzić do subtelnych błędów w kodzie.Rozważmy następujący przykład kodu.

[!UWAGA]

Z Option Strict Off, Visual Basic generuje ostrzeżenie kompilatora po implementacja interfejsu niejednoznaczne.Z Option Strict On, Visual Basic generuje błąd kompilatora.

' 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();
    }
}

W tym przykładzie jest nieokreślony sposób, w jaki pets.GetEnumerator metoda wybiera między Cat i Dog.Może to spowodować problemy w kodzie.

Zobacz też

Informacje

Korzystanie z wariancji dla delegatów Func i Action (C# i Visual Basic)

Koncepcje

Wariancje w interfejsach (C# i Visual Basic)