Algemene klassen (C#-programmeerhandleiding)
Algemene klassen bevatten bewerkingen die niet specifiek zijn voor een bepaald gegevenstype. Het meest voorkomende gebruik voor algemene klassen is met verzamelingen zoals gekoppelde lijsten, hashtabellen, stacks, wachtrijen, bomen, enzovoort. Bewerkingen zoals het toevoegen en verwijderen van items uit de verzameling worden in principe op dezelfde manier uitgevoerd, ongeacht het type gegevens dat wordt opgeslagen.
Voor de meeste scenario's waarvoor verzamelingsklassen zijn vereist, is de aanbevolen methode om de klassen te gebruiken die zijn opgegeven in de .NET-klassebibliotheek. Zie Algemene verzamelingen in .NET voor meer informatie over het gebruik van deze klassen.
Normaal gesproken maakt u algemene klassen door te beginnen met een bestaande concrete klasse en typen te wijzigen in typeparameters één voor één totdat u de optimale balans van generalisatie en bruikbaarheid bereikt. Bij het maken van uw eigen algemene klassen zijn belangrijke overwegingen onder andere:
Welke typen u wilt generaliseren in typeparameters.
Hoe meer typen u kunt parameteriseren, hoe flexibeler en herbruikbare uw code wordt. Te veel generalisatie kan echter code maken die moeilijk is voor andere ontwikkelaars om te lezen of te begrijpen.
Welke beperkingen, indien van toepassing, op de typeparameters (zie Beperkingen voor typeparameters).
Een goede regel is om de maximale beperkingen toe te passen waarmee u de typen kunt verwerken die u moet verwerken. Als u bijvoorbeeld weet dat uw algemene klasse alleen is bedoeld voor gebruik met verwijzingstypen, past u de klassebeperking toe. Hiermee voorkomt u onbedoeld gebruik van uw klasse met waardetypen en kunt u de
as
operatorT
gebruiken voor en controleren op null-waarden.Of het algemene gedrag in basisklassen en subklassen moet worden meegenomen.
Omdat algemene klassen als basisklassen kunnen fungeren, zijn dezelfde ontwerpoverwegingen hier van toepassing als bij niet-algemene klassen. Zie de regels voor het overnemen van algemene basisklassen verderop in dit onderwerp.
Of u nu een of meer algemene interfaces wilt implementeren.
Als u bijvoorbeeld een klasse ontwerpt die wordt gebruikt voor het maken van items in een verzameling op basis van generics, moet u mogelijk een interface implementeren, zoals IComparable<T> het
T
type klasse.
Zie Inleiding tot generics voor een voorbeeld van een eenvoudige algemene klasse.
De regels voor typeparameters en beperkingen hebben verschillende gevolgen voor algemeen klassegedrag, met name met betrekking tot overname en toegankelijkheid van leden. Voordat u doorgaat, moet u enkele termen begrijpen. Voor een algemene klasse Node<T>
kan de clientcode verwijzen naar de klasse door een typeargument op te geven : om een gesloten samengesteld type (Node<int>
) te maken, of door de typeparameter niet opgegeven te laten, bijvoorbeeld wanneer u een algemene basisklasse opgeeft, om een open samengesteld type (Node<T>
) te maken. Algemene klassen kunnen overnemen van beton-, gesloten of open geconstrueerde basisklassen:
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> { }
Niet-algemeen, met andere woorden, concrete klassen kunnen overnemen van gesloten samengestelde basisklassen, maar niet van geopende samengestelde klassen of van typeparameters, omdat er geen manier is tijdens runtime voor clientcode om het typeargument op te geven dat nodig is om de basisklasse te instantiëren.
//No error
class Node1 : BaseNodeGeneric<int> { }
//Generates an error
//class Node2 : BaseNodeGeneric<T> {}
//Generates an error
//class Node3 : T {}
Algemene klassen die overnemen van geopende samengestelde typen moeten typeargumenten opgeven voor parameters van het basisklassetype die niet worden gedeeld door de overnemende klasse, zoals wordt weergegeven in de volgende code:
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> {}
Algemene klassen die overnemen van geopende samengestelde typen moeten beperkingen opgeven die een superset zijn van, of impliceren, de beperkingen voor het basistype:
class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }
Algemene typen kunnen als volgt meerdere typeparameters en beperkingen gebruiken:
class SuperKeyType<K, V, U>
where U : System.IComparable<U>
where V : new()
{ }
Geopende en gesloten samengestelde typen kunnen worden gebruikt als methodeparameters:
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
}
Als een algemene klasse een interface implementeert, kunnen alle exemplaren van die klasse naar die interface worden gecast.
Algemene klassen zijn invariant. Met andere woorden, als een invoerparameter een List<BaseClass>
opgeeft, krijgt u een compilatietijdfout als u een List<DerivedClass>
.