Verwenden von Varianz in Schnittstellen für generische Auflistungen (C#)
Eine kovariante Schnittstelle ermöglicht den zugehörigen Methoden, mehr abgeleitete Typen zurückzugeben, als in der Schnittstelle angegeben sind. Eine kontravariante Schnittstelle ermöglicht den zugehörigen Methoden, Parameter von weniger abgeleiteten Typen anzunehmen, als in der Schnittstelle angegeben sind.
In .NET Framework 4 wurden mehrere vorhandene Schnittstellen kovariant und kontravariant. Dazu gehören IEnumerable<T> und IComparable<T>. Dadurch können Sie Methoden wiederverwenden, die mit generischen Auflistungen von Basistypen für Sammlungen von abgeleiteten Typen verwendet werden.
Die Liste der varianten Schnittstellen in .NET finden Sie unter Varianz in generischen Schnittstellen (C#).
Konvertieren von generischen Auflistungen
Das folgende Beispiel veranschaulicht die Vorteile der Unterstützung von Kovarianz in der IEnumerable<T>-Schnittstelle. Die PrintFullName
-Methode akzeptiert eine Auflistung vom Typ IEnumerable<Person>
als Parameter. Sie können dies jedoch für eine Auflistung des Typs IEnumerable<Employee>
wiederverwenden, da Employee
Person
erbt.
// Simple hierarchy of classes.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person { }
class Program
{
// The method has a parameter of the IEnumerable<Person> type.
public static void PrintFullName(IEnumerable<Person> persons)
{
foreach (Person person in persons)
{
Console.WriteLine("Name: {0} {1}",
person.FirstName, person.LastName);
}
}
public static void Test()
{
IEnumerable<Employee> employees = new List<Employee>();
// You can pass IEnumerable<Employee>,
// although the method expects IEnumerable<Person>.
PrintFullName(employees);
}
}
Vergleichen von generischen Auflistungen
Das folgende Beispiel veranschaulicht die Vorteile der Unterstützung von Kontravarianz in der IEqualityComparer<T>-Schnittstelle. Die PersonComparer
-Klasse implementiert die IEqualityComparer<Person>
-Schnittstelle. Sie können diese Klasse jedoch zum Vergleich einer Sequenz von Objekten des Typs Employee
wiederverwenden, da Employee
Person
erbt.
// Simple hierarchy of classes.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person { }
// The custom comparer for the Person type
// with standard implementations of Equals()
// and GetHashCode() methods.
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;
return x.FirstName == y.FirstName && x.LastName == y.LastName;
}
public int GetHashCode(Person person)
{
if (Object.ReferenceEquals(person, null)) return 0;
int hashFirstName = person.FirstName == null
? 0 : person.FirstName.GetHashCode();
int hashLastName = person.LastName.GetHashCode();
return hashFirstName ^ hashLastName;
}
}
class Program
{
public static void Test()
{
List<Employee> employees = new List<Employee> {
new Employee() {FirstName = "Michael", LastName = "Alexander"},
new Employee() {FirstName = "Jeff", LastName = "Price"}
};
// You can pass PersonComparer,
// which implements IEqualityComparer<Person>,
// although the method expects IEqualityComparer<Employee>.
IEnumerable<Employee> noduplicates =
employees.Distinct<Employee>(new PersonComparer());
foreach (var employee in noduplicates)
Console.WriteLine(employee.FirstName + " " + employee.LastName);
}
}