Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Často je užitečné definovat rozhraní pro obecné třídy kolekce nebo obecné třídy, které představují položky v kolekci. Abyste se vyhnuli operacím boxingu a rozbalení hodnotových typů, je lepší použít obecná rozhraní, jako je například IComparable<T>, u obecných tříd. Knihovna tříd .NET definuje několik obecných rozhraní pro použití s třídami kolekcí v System.Collections.Generic oboru názvů. Další informace o těchto rozhraních naleznete v tématu Obecná rozhraní.
Pokud je rozhraní zadáno jako omezení parametru typu, lze použít pouze typy, které implementují rozhraní. Následující příklad kódu ukazuje SortedList<T> třídu, která je odvozena z GenericList<T> třídy. Další informace naleznete v tématu Úvod do obecných typů.
SortedList<T> přidá omezení where T : IComparable<T>. Toto omezení umožňuje BubbleSort metodě SortedList<T> používat obecnou CompareTo metodu u elementů seznamu. V tomto příkladu jsou prvky seznamu jednoduchou třídou, Person která implementuje IComparable<Person>.
//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");
}
}
Jako omezení jednoho typu je možné zadat více rozhraní, a to následujícím způsobem:
class Stack<T> where T : System.IComparable<T>, IEnumerable<T>
{
}
Rozhraní může definovat více než jeden parametr typu následujícím způsobem:
interface IDictionary<K, V>
{
}
Pravidla dědičnosti, která se vztahují na třídy, platí také pro rozhraní:
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
Obecná rozhraní můžou dědit z ne generických rozhraní. V knihovně tříd .NET IEnumerable<T> dědí z IEnumerable. Když je obecné rozhraní děděno z negenerického rozhraní, parametr typu obvykle nahrazuje object v přepsaných členech. Například IEnumerable<T> používá T namísto object v návratové hodnotě GetEnumerator a v getteru vlastnosti Current. Protože T se používá pouze ve výstupních pozicích v těchto členech, IEnumerable<T> lze ho označit jako kovariantní. Pokud by T byl použit na vstupní pozici v přepisovaném členu, rozhraní by nemohlo být kovariantní a kompilátor by vyvolal chybu.
Konkrétní třídy mohou implementovat uzavřená konstruovaná rozhraní následujícím způsobem:
interface IBaseInterface<T> { }
class SampleClass : IBaseInterface<string> { }
Obecné třídy mohou implementovat obecná rozhraní nebo uzavřená konstruovaná rozhraní, pokud seznam parametrů třídy poskytuje všechny argumenty vyžadované rozhraním následujícím způsobem:
interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }
class SampleClass1<T> : IBaseInterface1<T> { } //No error
class SampleClass2<T> : IBaseInterface2<T, string> { } //No error
Pravidla, která řídí přetížení metod, jsou stejná pro metody v rámci obecných tříd, obecných struktur nebo obecných rozhraní. Další informace naleznete v tématu Obecné metody.
Rozhraní mohou deklarovat static abstract nebo static virtual členy. Rozhraní, která deklarují buď static abstract nebo static virtual členy, jsou téměř vždy obecná rozhraní. Kompilátor musí vyřešit volání a static virtualstatic abstract metody v době kompilace.
static virtual a static abstract metody deklarované v rozhraních nemají mechanismus runtime pro rozesílání, který by byl analogický k metodám virtual nebo abstract deklarovaným ve třídách. Místo toho kompilátor používá informace o typu dostupné v době kompilace. Tyto členy jsou obvykle deklarovány v obecných rozhraních. Kromě toho většina rozhraní, která deklarují nebo metody deklarujístatic virtual, že jeden z parametrů typu musí static abstract. Kompilátor pak pomocí zadaných argumentů typu přeloží typ deklarovaného člena.