Obecné třídy (Průvodce programováním v C#)

Obecné třídy zapouzdřují operace, které nejsou specifické pro konkrétní datový typ. Nejběžnějším použitím obecných tříd jsou kolekce, jako jsou propojené seznamy, hashovací tabulky, zásobníky, fronty, stromy atd. Operace, jako je přidávání a odebírání položek z kolekce, se v podstatě provádějí stejným způsobem bez ohledu na typ uložených dat.

Pro většinu scénářů, které vyžadují třídy kolekce, doporučujeme použít ty, které jsou k dispozici v knihovně tříd .NET. Další informace o použití těchto tříd naleznete v tématu Obecné kolekce v .NET.

Obvykle vytvoříte obecné třídy tak, že začnete s existující konkrétní třídou a změníte typy na parametry typu po jednom, dokud nedosáhnete optimální rovnováhy generalizace a použitelnosti. Při vytváření vlastních obecných tříd jsou důležité aspekty následující:

  • Které typy se mají generalizovat do parametrů typu.

    Obecně platí, že čím více typů můžete parametrizovat, tím flexibilnější a opakovaně použitelný kód se stane. Příliš mnoho zobecnění ale může vytvořit kód, který je pro ostatní vývojáře obtížně čitelný nebo srozumitelný.

  • Jaká omezení platí pro parametry typu (viz Omezení parametrů typu).

    Dobrým pravidlem je použít maximální možná omezení, která vám umožní zpracovávat typy, které musíte zpracovat. Pokud například víte, že vaše obecná třída je určena pouze pro použití s odkazovými typy, použijte omezení třídy. Tím zabráníte nezamýšlenému použití třídy s typy hodnot a umožníte použít operátor on asTa zkontrolovat hodnoty null.

  • Určuje, zda se má obecné chování začlenit do základních tříd a podtříd.

    Vzhledem k tomu, že obecné třídy mohou sloužit jako základní třídy, platí zde stejné aspekty návrhu jako u ne generických tříd. Podívejte se na pravidla týkající se dědění z obecných základních tříd dále v tomto tématu.

  • Ať už se má implementovat jedno nebo více obecných rozhraní.

    Pokud například navrhujete třídu, která se použije k vytváření položek v obecné kolekci, budete možná muset implementovat rozhraní, například IComparable<T> kde T je typ vaší třídy.

Příklad jednoduché obecné třídy naleznete v tématu Úvod do obecných typů.

Pravidla pro parametry typů a omezení mají několik důsledků pro obecné chování třídy, zejména pokud jde o dědičnost a přístupnost členů. Než budete pokračovat, měli byste rozumět některým termínem. Kód klienta obecné třídy Node<T>, může odkazovat na třídu zadáním argumentu typu – vytvoření uzavřeného konstruovaného typu (Node<int>) nebo ponecháním parametru typu nezadaného – například při zadání obecné základní třídy, vytvoření otevřeného konstruovaného typu (Node<T>). Obecné třídy mohou dědit z betonu, uzavřené konstruované nebo otevřené vytvořené základní třídy:

class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { }

Jinými slovy, beton, třídy mohou dědit z uzavřených konstruovaných základních tříd, ale ne z otevřených konstruovaných tříd nebo z parametrů typu, protože neexistuje způsob, jak v době běhu klientského kódu zadat argument typu potřebný k vytvoření instance základní třídy.

//No error
class Node1 : BaseNodeGeneric<int> { }

//Generates an error
//class Node2 : BaseNodeGeneric<T> {}

//Generates an error
//class Node3 : T {}

Obecné třídy, které dědí z otevřených konstruovaných typů, musí poskytovat argumenty typu pro všechny parametry základní třídy, které nejsou sdíleny zděděnou třídou, jak je znázorněno v následujícím kódu:

class BaseNodeMultiple<T, U> { }

//No error
class Node4<T> : BaseNodeMultiple<T, int> { }

//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {}

Obecné třídy, které dědí z otevřených konstruovaných typů, musí určovat omezení, která jsou nadmnožinou nebo znamenají omezení základního typu:

class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }

Obecné typy můžou používat více parametrů typů a omezení, a to následujícím způsobem:

class SuperKeyType<K, V, U>
    where U : System.IComparable<U>
    where V : new()
{ }

Otevřené vytvořené a uzavřené konstruované typy lze použít jako parametry metody:

void Swap<T>(List<T> list1, List<T> list2)
{
    //code to swap items
}

void Swap(List<int> list1, List<int> list2)
{
    //code to swap items
}

Pokud obecná třída implementuje rozhraní, všechny instance této třídy lze přetypovat na toto rozhraní.

Obecné třídy jsou invariantní. Jinými slovy, pokud vstupní parametr určuje List<BaseClass>, zobrazí se chyba v době kompilace, pokud se pokusíte poskytnout List<DerivedClass>.

Viz také