Untersuchen Sie Varianz bei Delegaten

Abgeschlossen

Die Varianz in C# ermöglicht es Ihnen, flexiblere Methodensignaturen mit Delegaten zu verwenden. Sie können Stellvertretungen Methoden zuweisen, auch wenn deren Parametertypen oder Rückgabetypen nicht identisch sind, solange sie bestimmten Regeln entsprechen. Varianz ist nützlich, wenn Sie mit Vererbungshierarchien arbeiten.

Was ist Varianz?

Varianz ist ein Konzept in C#, mit dem Sie einen typabhängigeren Typ verwenden können als den, der in einem Delegat oder einer Schnittstelle angegeben ist. Dadurch können Sie flexibleren und wiederverwendbaren Code erstellen, indem Methoden Stellvertretungen zugewiesen werden können, auch wenn ihre Signaturen nicht exakt übereinstimmen.

Es gibt zwei Arten von Varianz:

  • Kovarianz: Ermöglicht einer Methode, einen Rückgabetyp zu haben, der mehr abgeleitet ist als der im Delegaten definierte Typ.
  • Kontravarianz: Ermöglicht einer Methode, Parameter zu akzeptieren, die weniger abgeleitet sind als die Parameter im Delegattyp.

Die Varianzunterstützung umfasst generische und nichtgenerische Delegate.

Varianz mit benutzerdefinierten Delegaten

Benutzerdefinierte Delegate sind benutzerspezifische Delegattypen. Varianz kann auf benutzerdefinierte Delegate angewendet werden, um sie flexibler zu gestalten.

Im folgenden Beispiel wird die Verwendung der Varianz mit benutzerdefinierten Delegaten veranschaulicht:


public class Animal { }
public class Dog : Animal { }

// Define a delegate that takes a Dog and returns an Animal
public delegate Animal AnimalDelegate(Dog dog);

// Method that matches the delegate signature
public static Animal GetAnimal(Dog dog) => new Animal();

// Method that uses covariance (returns a more derived type)
public static Dog GetDog(Dog dog) => new Dog();

// Method that uses contravariance (accepts a less derived type)
public static Animal GetAnimalFromAnimal(Animal animal) => new Animal();

public class Program
{
    public static void Main()
    {
        AnimalDelegate del;

        // Assign method with matching signature
        del = GetAnimal;
        Animal animal = del(new Dog());

        // Assign method with covariant return type
        del = GetDog;
        animal = del(new Dog());

        // Assign method with contravariant parameter type
        del = GetAnimalFromAnimal;
        animal = del(new Dog());
    }
}

In diesem Beispiel kann GetDogAnimalDelegate zugewiesen werden, da Dog ein mehr abgeleiteter Typ als Animal ist (Kovarianz). Ebenso kann GetAnimalFromAnimal zugewiesen werden, da Animal ein weniger abgeleiteter Typ als Dog ist (Kontravarianz).

Varianz mit stark typierten Delegaten

Die Varianz gilt auch für stark typierte Delegate wie Action und Func.

Kovarianz mit Func

Im folgenden Beispiel wird die Kovarianz mit Func veranschaulicht.


public class Person { }
public class Employee : Person { }

public static Employee FindEmployee(string title) => new Employee();

public class Program
{
    public static void Main()
    {
        // Func<string, Person> can hold a method that returns Employee
        Func<string, Person> func = FindEmployee;
        Person person = func("Manager");
    }
}

In diesem Beispiel gibt FindEmployee ein Employee zurück, das stärker abgeleitet ist als Person, sodass es Func<string, Person> zugewiesen werden kann.

Kontravarianz mit Action

Im folgenden Beispiel wird die Kontravarianz mit Action demonstriert.


public class Animal { }
public class Dog : Animal { }

public static void HandleAnimal(Animal animal) { }

public class Program
{
    public static void Main()
    {
        // Action<Dog> can hold a method that takes Animal
        Action<Dog> action = HandleAnimal;
        action(new Dog());
    }
}

Hier akzeptiert HandleAnimal eine Animal, die weniger abgeleitet ist als Dog, so dass sie Action<Dog> zugewiesen werden kann.

Varianz in Generika

Die Varianz kann auch auf generische Typparameter mit den in und out Schlüsselwörtern angewendet werden.

Generischer Kovarianttyp

Das out Schlüsselwort wird verwendet, um einen kovarianten generischen Typparameter zu deklarieren. Mit diesem Feature können Sie einen abgeleiteten Typ als Rückgabetyp verwenden.

Im folgenden Beispiel wird die Kovarianz in Generika veranschaulicht:


public interface ICovariant<out T> { T Get(); }

public class Animal { }
public class Dog : Animal { }

public class CovariantExample : ICovariant<Dog>
{
    public Dog Get() => new Dog();
}

public class Program
{
    public static void Main()
    {
        ICovariant<Animal> covariant = new CovariantExample();
        Animal animal = covariant.Get();
    }
}


In diesem Beispiel erlaubt ICovariant<out T> die Zuweisung von ICovariant<Dog> zu ICovariant<Animal>, weil Dog stärker abgeleitet ist als Animal.

Generischer Kontravarianttyp

Das in Schlüsselwort wird zum Deklarieren eines kontravarianten generischen Typparameters verwendet. Mit diesem Feature können Sie einen weniger abgeleiteten Typ als Parametertyp verwenden.

Im folgenden Beispiel wird die Kontravarianz in Generika veranschaulicht:


public interface IContravariant<in T> { void Set(T value); }

public class Animal { }
public class Dog : Animal { }

public class ContravariantExample : IContravariant<Animal>
{
    public void Set(Animal value) { }
}

public class Program
{
    public static void Main()
    {
        IContravariant<Dog> contravariant = new ContravariantExample();
        contravariant.Set(new Dog());
    }
}

Hier ermöglicht IContravariant<in T>, dass IContravariant<Animal>IContravariant<Dog> zugewiesen wird, weil Animal weniger abgeleitet ist als Dog.

Wichtige Punkte

  • Mit Varianz können Sie flexiblere Methodensignaturen mit Delegaten verwenden.
  • Kovarianz ermöglicht einer Methode, einen Rückgabetyp zu haben, der mehr abgeleitet ist als der im Delegaten definierte Typ.
  • Kontravarianz ermöglicht einer Methode, Parameter zu akzeptieren, die weniger abgeleitet sind als die Parameter im Delegattyp.
  • Benutzerdefinierte und stark typisierte Delegate können die Varianz verwenden, um flexibler zu sein.
  • Generika können mit den Schlüsselwörtern in und out Varianz verwenden, um kontravariante und kovariante generische Typparameter zu deklarieren.