Verwenden von Varianz in Schnittstellen für generische Auflistungen (C# und Visual Basic)
Eine kovariante Schnittstelle ermöglicht den zugehörigen Methoden, stärker abgeleitete Typen zurückzugeben, als in der Schnittstelle angegeben wurden. Eine kontravariante Schnittstelle ermöglicht den zugehörigen Methoden, Parameter von weniger stark abgeleiteten Typen zu akzeptieren, als in der Schnittstelle angegeben wurden.
In .NET Framework 4 werden mehrere vorhandene Schnittstellen ko- und kontravariant. Dazu gehören IEnumerable<T> und IComparable<T>. Dies ermöglicht es Ihnen, Methoden wiederzuverwenden, die generische Auflistungen von Basistypen für Auflistungen von abgeleiteten Typen verwenden.
Eine Liste der varianten Schnittstellen in .NET Framework finden Sie unter Varianz in generischen Schnittstellen (C# und Visual Basic).
Konvertieren von generischen Auflistungen
Im folgenden Beispiel werden die Vorteile der Kovarianzunterstützung in der IEnumerable<T>-Schnittstelle veranschaulicht. Die PrintFullName-Methode akzeptiert eine Auflistung vom Typ IEnumerable<Person> (IEnumerable(Of Person) in Visual Basic) als Parameter. Sie können sie jedoch für eine Auflistung vom Typ IEnumerable<Employee> (IEnumerable(Of Person) in Visual Basic) wiederverwenden, da Employee den Typ Person erbt.
' Simple hierarchy of classes.
Public Class Person
Public Property FirstName As String
Public Property LastName As String
End Class
Public Class Employee
Inherits Person
End Class
' The method has a parameter of the IEnumerable(Of Person) type.
Public Sub PrintFullName(ByVal persons As IEnumerable(Of Person))
For Each person As Person In persons
Console.WriteLine(
"Name: " & person.FirstName & " " & person.LastName)
Next
End Sub
Sub Main()
Dim employees As IEnumerable(Of Employee) = New List(Of Employee)
' You can pass IEnumerable(Of Employee),
' although the method expects IEnumerable(Of Person).
PrintFullName(employees)
End Sub
// 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
Im folgenden Beispiel werden die Vorteile der Kontravarianzunterstützung in der IComparer<T>-Schnittstelle veranschaulicht. Die PersonComparer-Klasse implementiert die IComparer<Person>-Schnittstelle (IComparer(Of Person) in Visual Basic). Sie können diese Klasse jedoch wiederverwenden, um eine Folge von Objekten vom Typ Employee zu vergleichen, da Employee den Typ Person erbt.
' Simple hierarhcy of classes.
Public Class Person
Public Property FirstName As String
Public Property LastName As String
End Class
Public Class Employee
Inherits Person
End Class
' The custom comparer for the Person type
' with standard implementations of Equals()
' and GetHashCode() methods.
Class PersonComparer
Implements IEqualityComparer(Of Person)
Public Function Equals1(
ByVal x As Person,
ByVal y As Person) As Boolean _
Implements IEqualityComparer(Of Person).Equals
If x Is y Then Return True
If x Is Nothing OrElse y Is Nothing Then Return False
Return (x.FirstName = y.FirstName) AndAlso
(x.LastName = y.LastName)
End Function
Public Function GetHashCode1(
ByVal person As Person) As Integer _
Implements IEqualityComparer(Of Person).GetHashCode
If person Is Nothing Then Return 0
Dim hashFirstName =
If(person.FirstName Is Nothing,
0, person.FirstName.GetHashCode())
Dim hashLastName = person.LastName.GetHashCode()
Return hashFirstName Xor hashLastName
End Function
End Class
Sub Main()
Dim employees = New List(Of Employee) From {
New Employee With {.FirstName = "Michael", .LastName = "Alexander"},
New Employee With {.FirstName = "Jeff", .LastName = "Price"}
}
' You can pass PersonComparer,
' which implements IEqualityComparer(Of Person),
' although the method expects IEqualityComparer(Of Employee)
Dim noduplicates As IEnumerable(Of Employee) = employees.Distinct(New PersonComparer())
For Each employee In noduplicates
Console.WriteLine(employee.FirstName & " " & employee.LastName)
Next
End Sub
// 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);
}
}