Share via


Allmänna klasser (C#-programmeringsguide)

Allmänna klasser kapslar in åtgärder som inte är specifika för en viss datatyp. Den vanligaste användningen för generiska klasser är med samlingar som länkade listor, hash-tabeller, staplar, köer, träd och så vidare. Åtgärder som att lägga till och ta bort objekt från samlingen utförs i princip på samma sätt oavsett vilken typ av data som lagras.

I de flesta scenarier som kräver samlingsklasser är den rekommenderade metoden att använda de som anges i .NET-klassbiblioteket. Mer information om hur du använder dessa klasser finns i Allmänna samlingar i .NET.

Vanligtvis skapar du allmänna klasser genom att börja med en befintlig betongklass och ändra typer till typparametrar en i taget tills du når den optimala balansen mellan generalisering och användbarhet. När du skapar dina egna allmänna klasser är följande viktiga saker att tänka på:

  • Vilka typer som ska generaliseras till typparametrar.

    Ju fler typer du kan parametrisera, desto mer flexibel och återanvändbar blir koden. Men för mycket generalisering kan skapa kod som är svår för andra utvecklare att läsa eller förstå.

  • Vilka eventuella begränsningar som ska gälla för typparametrarna (se Begränsningar för typparametrar).

    En bra regel är att tillämpa maximalt möjliga begränsningar som fortfarande låter dig hantera de typer som du måste hantera. Om du till exempel vet att den generiska klassen endast är avsedd för användning med referenstyper ska du tillämpa klassbegränsningen. Det förhindrar oavsiktlig användning av klassen med värdetyper och gör att du kan använda operatorn asToch söka efter null-värden.

  • Om generiskt beteende ska räknas in i basklasser och underklasser.

    Eftersom generiska klasser kan fungera som basklasser gäller samma designöverväganden här som med icke-generiska klasser. Se reglerna om att ärva från generiska basklasser senare i det här avsnittet.

  • Om du vill implementera ett eller flera allmänna gränssnitt.

    Om du till exempel utformar en klass som ska användas för att skapa objekt i en generisk samling kan du behöva implementera ett gränssnitt, till exempel IComparable<T> var T är klassens typ.

Ett exempel på en enkel generisk klass finns i Introduktion till generiska objekt.

Reglerna för typparametrar och begränsningar har flera konsekvenser för generiskt klassbeteende, särskilt när det gäller arvs- och medlemstillgänglighet. Innan du fortsätter bör du förstå vissa termer. För en allmän klassklientkod Node<T>, kan referera till klassen antingen genom att ange ett typargument – för att skapa en sluten konstruktionstyp (Node<int>), eller genom att lämna typparametern ospecificerad , till exempel när du anger en allmän basklass, för att skapa en öppen konstruktionstyp (Node<T>). Generiska klasser kan ärva från betong, stängda konstruktioner eller öppna konstruerade basklasser:

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> { }

Icke-generiska, med andra ord, betongklasser kan ärva från stängda konstruerade basklasser, men inte från öppna konstruerade klasser eller från typparametrar eftersom det inte finns något sätt vid körning för klientkod att ange det typargument som krävs för att instansiera basklassen.

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

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

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

Generiska klasser som ärver från öppna konstruerade typer måste ange typargument för alla basklasstypparametrar som inte delas av ärvningsklassen, vilket visas i följande kod:

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> {}

Allmänna klasser som ärver från öppna konstruerade typer måste ange begränsningar som är en supermängd av, eller innebär, begränsningarna för bastypen:

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

Allmänna typer kan använda flera typparametrar och begränsningar enligt följande:

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

Öppna konstruerade och stängda konstruktionstyper kan användas som metodparametrar:

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
}

Om en allmän klass implementerar ett gränssnitt kan alla instanser av den klassen överföras till det gränssnittet.

Generiska klasser är invarianta. Om en indataparameter med andra ord anger ett List<BaseClass>får du ett kompileringsfel om du försöker ange en List<DerivedClass>.

Se även