Általános osztályok (C# programozási útmutató)
Az általános osztályok olyan műveleteket foglalnak össze, amelyek nem egy adott adattípusra vonatkoznak. Az általános osztályok leggyakrabban olyan gyűjteményeket használnak, mint a csatolt listák, kivonattáblák, halmok, üzenetsorok, fák stb. Az olyan műveletek, mint például az elemek felvétele és eltávolítása a gyűjteményből, alapvetően ugyanúgy hajthatók végre, függetlenül a tárolt adatok típusától.
A gyűjteményosztályokat igénylő legtöbb forgatókönyv esetében az ajánlott módszer a .NET-osztálytárban megadottak használata. További információ az osztályok használatáról: Általános gyűjtemények a .NET-ben.
Az általános osztályokat általában egy meglévő konkrét osztálytól kezdve, a típusparaméterek típusparaméterekké alakításával hozza létre, amíg el nem éri az általánosítás és a használhatóság optimális egyensúlyát. A saját általános osztályok létrehozásakor fontos szempontok a következők:
Mely típusokat kell általánosítani típusparaméterekké.
Általában minél több típust lehet paraméterezni, annál rugalmasabbá és újrafelhasználhatóbbá válik a kód. A túl sok általánosítás azonban olyan kódot hozhat létre, amelyet más fejlesztők nehezen tudnak olvasni vagy értelmezni.
Milyen kényszerek alkalmazhatók a típusparaméterekre (lásd a típusparaméterekre vonatkozó korlátozásokat).
Jó szabály a lehetséges maximális korlátozások alkalmazása, amelyek továbbra is lehetővé teszik a kezelni kívánt típusok kezelését. Ha például tudja, hogy az általános osztály csak referenciatípusokhoz használható, alkalmazza az osztálykorlátozást. Ez megakadályozza az osztály nem kívánt használatát értéktípusokkal, és lehetővé teszi, hogy az
as
operátortT
használja, és null értékeket keressen.Azt határozza meg, hogy az általános viselkedést alaposztályokba és alosztályokba kell-e számolni.
Mivel az általános osztályok alaposztályként szolgálhatnak, itt is ugyanazok a tervezési szempontok érvényesek, mint a nem általános osztályokra. A témakör későbbi részében megismeri az általános alaposztályoktól való öröklődésre vonatkozó szabályokat.
Egy vagy több általános felület implementálása.
Ha például olyan osztályt tervez, amely egy általános gyűjtemény elemeinek létrehozásához lesz felhasználva, előfordulhat, hogy egy felületet kell implementálnia, például IComparable<T> azt, hogy hol
T
található az osztály típusa.
Egy egyszerű általános osztályra példa: Bevezetés a generics használatába.
A típusparaméterekre és korlátozásokra vonatkozó szabályok számos hatással vannak az általános osztály viselkedésére, különösen az öröklésre és a tagok akadálymentességére. A folytatás előtt meg kell értenie néhány kifejezést. Általános osztály Node<T>
esetén az ügyfélkód egy típusargumentum megadásával hivatkozhat az osztályra – zárt, konstruktált típus (Node<int>
); vagy a típusparaméter meghatározatlan elhagyásával – például egy általános alaposztály megadásakor, egy nyitott strukturált típus (Node<T>
) létrehozásához. Az általános osztályok öröklődhetnek betonból, zárt vagy nyitott épített alaposztályokból:
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> { }
A nem általános, más szóval konkrét osztályok örökölhetnek zárt, konstruktált alaposztályokból, nyitott, konstruktált osztályokból vagy típusparaméterekből azonban nem, mert az ügyfélkód futásidőben nem tudja megadni az alaposztály példányosításához szükséges típusargumentumot.
//No error
class Node1 : BaseNodeGeneric<int> { }
//Generates an error
//class Node2 : BaseNodeGeneric<T> {}
//Generates an error
//class Node3 : T {}
A nyíltan létrehozott típusoktól öröklő általános osztályoknak típusargumentumokat kell megadniuk minden olyan alaposztálytípus-paraméterhez, amelyet az öröklő osztály nem oszt meg, ahogyan az az alábbi kódban is látható:
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> {}
A nyitott, konstruktált típusoktól öröklő általános osztályoknak olyan kényszereket kell megadniuk, amelyek az alaptípusra vonatkozó kényszerek szuperhalmazai, vagy ezekre utalnak:
class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }
Az általános típusok több típusparamétert és korlátozást is használhatnak az alábbiak szerint:
class SuperKeyType<K, V, U>
where U : System.IComparable<U>
where V : new()
{ }
A nyitott és zárt építésű típusok metódusparaméterekként használhatók:
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
}
Ha egy általános osztály implementál egy felületet, az osztály összes példánya az adott felületre vethető.
Az általános osztályok invariánsak. Más szóval, ha egy bemeneti paraméter ad meg egy List<BaseClass>
értéket, fordítási idő hibát fog kapni, ha megpróbál megadni egy List<DerivedClass>
.