Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Es ist häufig hilfreich, Schnittstellen für generische Auflistungsklassen oder für die generischen Klassen zu definieren, die Elemente in der Auflistung darstellen. Um Boxing- und Unboxing-Operationen für Werttypen zu vermeiden, ist es besser, generische Schnittstellen wie IComparable<T> für generische Klassen zu verwenden. Die .NET-Klassenbibliothek definiert mehrere generische Schnittstellen für die Verwendung mit den Auflistungsklassen im System.Collections.Generic Namespace. Weitere Informationen zu diesen Schnittstellen finden Sie unter generische Schnittstellen.
Wenn eine Schnittstelle als Einschränkung für einen Typparameter angegeben wird, können nur Typen verwendet werden, die die Schnittstelle implementieren. Das folgende Codebeispiel zeigt eine SortedList<T>
Klasse, die von der GenericList<T>
Klasse abgeleitet wird. Weitere Informationen finden Sie in der Einführung in Generika.
SortedList<T>
fügt die Einschränkung where T : IComparable<T>
hinzu. Diese Einschränkung ermöglicht es der BubbleSort
Methode in SortedList<T>
, die generische CompareTo Methode für Listenelemente zu verwenden. In diesem Beispiel sind Listenelemente eine einfache Klasse, Person
, die IComparable<Person>
implementiert.
//Type parameter T in angle brackets.
public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
protected Node head;
protected Node current = null;
// Nested class is also generic on T
protected class Node
{
public Node next;
private T data; //T as private member datatype
public Node(T t) //T used in non-generic constructor
{
next = null;
data = t;
}
public Node Next
{
get { return next; }
set { next = value; }
}
public T Data //T as return type of property
{
get { return data; }
set { data = value; }
}
}
public GenericList() //constructor
{
head = null;
}
public void AddHead(T t) //T as method parameter type
{
Node n = new Node(t);
n.Next = head;
head = n;
}
// Implementation of the iterator
public System.Collections.Generic.IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
// IEnumerable<T> inherits from IEnumerable, therefore this class
// must implement both the generic and non-generic versions of
// GetEnumerator. In most cases, the non-generic method can
// simply call the generic method.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
{
// A simple, unoptimized sort algorithm that
// orders list elements from lowest to highest:
public void BubbleSort()
{
if (null == head || null == head.Next)
{
return;
}
bool swapped;
do
{
Node previous = null;
Node current = head;
swapped = false;
while (current.next != null)
{
// Because we need to call this method, the SortedList
// class is constrained on IComparable<T>
if (current.Data.CompareTo(current.next.Data) > 0)
{
Node tmp = current.next;
current.next = current.next.next;
tmp.next = current;
if (previous == null)
{
head = tmp;
}
else
{
previous.next = tmp;
}
previous = tmp;
swapped = true;
}
else
{
previous = current;
current = current.next;
}
}
} while (swapped);
}
}
// A simple class that implements IComparable<T> using itself as the
// type argument. This is a common design pattern in objects that
// are stored in generic lists.
public class Person : System.IComparable<Person>
{
string name;
int age;
public Person(string s, int i)
{
name = s;
age = i;
}
// This will cause list elements to be sorted on age values.
public int CompareTo(Person p)
{
return age - p.age;
}
public override string ToString()
{
return name + ":" + age;
}
// Must implement Equals.
public bool Equals(Person p)
{
return (this.age == p.age);
}
}
public class Program
{
public static void Main()
{
//Declare and instantiate a new generic SortedList class.
//Person is the type argument.
SortedList<Person> list = new SortedList<Person>();
//Create name and age values to initialize Person objects.
string[] names =
[
"Franscoise",
"Bill",
"Li",
"Sandra",
"Gunnar",
"Alok",
"Hiroyuki",
"Maria",
"Alessandro",
"Raul"
];
int[] ages = [45, 19, 28, 23, 18, 9, 108, 72, 30, 35];
//Populate the list.
for (int x = 0; x < 10; x++)
{
list.AddHead(new Person(names[x], ages[x]));
}
//Print out unsorted list.
foreach (Person p in list)
{
System.Console.WriteLine(p.ToString());
}
System.Console.WriteLine("Done with unsorted list");
//Sort the list.
list.BubbleSort();
//Print out sorted list.
foreach (Person p in list)
{
System.Console.WriteLine(p.ToString());
}
System.Console.WriteLine("Done with sorted list");
}
}
Mehrere Schnittstellen können wie folgt als Einschränkungen für einen einzelnen Typ angegeben werden:
class Stack<T> where T : System.IComparable<T>, IEnumerable<T>
{
}
Eine Schnittstelle kann mehrere Typparameter wie folgt definieren:
interface IDictionary<K, V>
{
}
Die Regeln der Vererbung, die für Klassen gelten, gelten auch für Schnittstellen:
interface IMonth<T> { }
interface IJanuary : IMonth<int> { } //No error
interface IFebruary<T> : IMonth<int> { } //No error
interface IMarch<T> : IMonth<T> { } //No error
//interface IApril<T> : IMonth<T, U> {} //Error
Generische Schnittstellen können von nicht generischen Schnittstellen erben, wenn die generische Schnittstelle kovariant ist, was bedeutet, dass er nur seinen Typparameter als Rückgabewert verwendet. In der .NET-Klassenbibliothek erbt IEnumerable<T> von IEnumerable, da IEnumerable<T> nur T
im Rückgabewert von GetEnumerator und im Eigenschafts-Getter von Current verwendet.
Konkrete Klassen können geschlossene konstruierte Schnittstellen wie folgt implementieren:
interface IBaseInterface<T> { }
class SampleClass : IBaseInterface<string> { }
Generische Klassen können generische Schnittstellen oder geschlossene konstruierte Schnittstellen implementieren, solange die Klassenparameterliste alle argumente bereitstellt, die von der Schnittstelle benötigt werden, wie folgt:
interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }
class SampleClass1<T> : IBaseInterface1<T> { } //No error
class SampleClass2<T> : IBaseInterface2<T, string> { } //No error
Die Regeln, die die Methodenüberladung steuern, sind für Methoden innerhalb generischer Klassen, generischer Strukturen oder generischer Schnittstellen identisch. Weitere Informationen finden Sie unter generische Methoden.
Beginnend mit C# 11 können Schnittstellen static abstract
oder static virtual
Member deklarieren. Schnittstellen, die entweder static abstract
oder static virtual
Member deklarieren, sind fast immer generische Schnittstellen. Der Compiler muss Aufrufe von static virtual
- und static abstract
-Methoden zur Kompilierzeit auflösen.
static virtual
und static abstract
methoden, die in Schnittstellen deklariert sind, verfügen nicht über einen Laufzeit-Dispatch-Mechanismus analog zu virtual
oder abstract
methoden, die in Klassen deklariert sind. Stattdessen verwendet der Compiler Typinformationen, die zur Kompilierzeit verfügbar sind. Diese Member werden in der Regel in generischen Schnittstellen deklariert. Darüber hinaus deklarieren die meisten Schnittstellen, die static virtual
- oder static abstract
-Methoden deklarieren, dass einer der Typparameter die deklarierte Schnittstelle implementieren muss. Der Compiler verwendet dann die angegebenen Typargumente, um den Typ des deklarierten Elements aufzulösen.