Megosztás a következőn keresztül:


A C# típusú rendszer

A C# egy erősen gépelt nyelv. Minden változónak és állandónak van egy típusa, ahogy minden olyan kifejezésnek, amely értéket ad vissza. Minden metódusdeklaráció megadja az egyes bemeneti paraméterek és a visszatérési érték nevét, típusát és típusát (érték, hivatkozás vagy kimenet). A .NET osztálykódtár olyan beépített numerikus típusokat és összetett típusokat határoz meg, amelyek számos konstrukciót képviselnek. Ilyenek például a fájlrendszer, a hálózati kapcsolatok, az objektumok gyűjteményei és tömbjei, valamint a dátumok. Egy tipikus C#-program az osztálytár és a felhasználó által definiált típusok típusait használja, amelyek a program problémás tartományára jellemző fogalmakat modellezhetik.

A típusban tárolt információk a következő elemeket tartalmazhatják:

  • Az a tárterület, amelyet egy adott típusú változó igényel.
  • A maximális és minimális értékek, amelyeket képviselhet.
  • A benne szereplő tagok (metódusok, mezők, események stb.).
  • Az alaptípus, amelytől örököl.
  • Az általa implementálva lévő felületek.
  • Az engedélyezett műveletek.

A fordító típusinformációkat használ annak biztosításához, hogy a kódban végrehajtott összes művelet típusa biztonságos legyen. Ha például deklarál egy típusú intváltozót, a fordító lehetővé teszi, hogy a változót kiegészítő és kivonási műveletekben használja. Ha ugyanezt a műveletet egy változótípuson boolpróbálja végrehajtani, a fordító hibát generál, ahogyan az alábbi példában látható:

int a = 5;
int b = a + 2; //OK

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;

Megjegyzés:

C és C++ fejlesztők, vegyétek észre, hogy C#-ban bool nem konvertálható int-re.

A fordító metaadatokként ágyazza be a típusadatokat a végrehajtható fájlba. A közös nyelvi futtatókörnyezet (CLR) a metaadatokat futásidőben használja a típusbiztonság további garantálásához, amikor lefoglalja és visszanyeri a memóriát.

Típusok megadása változódeklarációkban

Amikor egy programban deklarál egy változót vagy állandót, meg kell adnia annak típusát, vagy a var kulcsszó használatával lehetővé kell tennie, hogy a fordító a típust következtethesse. Az alábbi példa néhány olyan változódeklarációt mutat be, amelyek beépített numerikus típusokat és összetett, felhasználó által definiált típusokat egyaránt használnak:

// Declaration only:
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = [0, 1, 2, 3, 4, 5];
var query = from item in source
            where item <= limit
            select item;

A metódusparaméterek és a visszatérési értékek típusait a metódus deklarációja határozza meg. Az alábbi aláírás egy olyan metódust mutat be, amely bemeneti argumentumot igényel int , és egy sztringet ad vissza:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else
        return String.Empty;
}
private string[] names = ["Spencer", "Sally", "Doug"];

Miután deklarált egy változót, nem állíthatja újra újra egy új típussal, és nem rendelhet hozzá olyan értéket, amely nem kompatibilis a deklarált típusával. Például nem deklarálhat egy int, majd rendelhet hozzá egy logikai értéket true. Az értékek azonban más típusúvá alakíthatók, például új változókhoz rendelve vagy metódusargumentumként átadva. A fordító automatikusan elvégzi az adatvesztést nem okozó típuskonverziót . Az olyan átalakítás, amely adatvesztést okozhat, konverziót igényel a forráskódban.

További információ: Casting and Type Conversions.

Beépített típusok

A C# beépített típusok standard készletét biztosítja. Ezek egész számokat, lebegőpontos értékeket, logikai kifejezéseket, szöveges karaktereket, decimális értékeket és más típusú adatokat jelölnek. Vannak beépített string és object típusok is. Ezek a típusok bármilyen C#-programban használhatók. A beépített típusok teljes listáját a Beépített típusok című témakörben találja.

Egyéni típusok

A struct, class, interface, enumés record szerkezetekkel hozhatja létre saját egyéni típusait. Maga a .NET-osztálytár olyan egyéni típusok gyűjteménye, amelyeket a saját alkalmazásaiban használhat. Alapértelmezés szerint az osztálytár leggyakrabban használt típusai bármely C#-programban elérhetők. Mások csak akkor válnak elérhetővé, ha kifejezetten hozzáad egy projekthivatkozást az őket definiáló szerelvényhez. Miután a fordító hivatkozik a szerelvényre, deklarálhatja az adott szerelvényben deklarált típusok változóit (és állandóit) a forráskódban. További információ: .NET Osztálytár.

A típus meghatározásakor az egyik első döntés az, hogy eldönti, melyik szerkezetet használja a típushoz. Az alábbi lista segít a kezdeti döntés meghozatalában. Átfedés van a választási lehetőségek között. A legtöbb esetben több lehetőség is ésszerű választás.

  • Ha az adattároló mérete kicsi, legfeljebb 64 bájt, válasszon egy struct vagy record struct.
  • Ha a típus nem módosítható, vagy nem strukturált mutációt szeretne, válasszon egy struct vagy record struct.
  • Ha a típusnak értékszemantikával kell rendelkeznie az egyenlőség szempontjából, válasszon egy record class vagy record struct.
  • Ha a típus elsősorban adatok tárolására szolgál, nem viselkedésre, válasszon egy record class vagy record struct.
  • Ha a típus egy öröklési hierarchia része, válasszon egy record class vagy egy class.
  • Ha a típus polimorfizmust használ, válasszon egy class.
  • Ha az elsődleges cél a viselkedés, válasszon egy class.

A gyakori típusrendszer

Fontos megérteni a .NET típusrendszerével kapcsolatos két alapvető pontot:

  • Támogatja az öröklés elvét. A típusok más, úgynevezett alaptípusokból is származhatnak. A származtatott típus örökli (bizonyos korlátozásokkal) az alaptípus metódusait, tulajdonságait és más tagjait. Az alaptípus más típusból is származhat, ebben az esetben a származtatott típus örökli mindkét alaptípus tagjait az öröklési hierarchiában. Minden típus, beleértve a beépített numerikus típusokat, például System.Int32 a (C# kulcsszó: int) is, végső soron egyetlen alaptípusból származik, azaz System.Object (C# kulcsszó: object). Ezt az egyesített típushierarchiát common type system (CTS) néven nevezik. További információ a C#-beli öröklésről: Öröklés.
  • A CTS minden típusa értéktípusként vagy referenciatípusként van definiálva. Ezek a típusok tartalmazzák a .NET-osztálytár összes egyéni típusát, valamint a saját, felhasználó által definiált típusait is. A struct kulcsszóval definiált típusok értéktípusok; az összes beépített numerikus típus is ebbe a kategóriába tartozik.structs A class vagy record kulcsszó használatával definiált típusok referenciatípusok. A referenciatípusok és az értéktípusok különböző fordítási idejű szabályokkal és különböző futásidejű viselkedési módokkal rendelkeznek.

Az alábbi ábra az értéktípusok és a referenciatípusok közötti kapcsolatot mutatja be a CTS-ben.

A CTS értéktípusait és referenciatípusait bemutató képernyőkép.

Megjegyzés:

Láthatja, hogy a leggyakrabban használt típusok mindegyike a System névtérben van rendszerezve. A típust tartalmazó névtérnek azonban nincs köze ahhoz, hogy értéktípusról vagy hivatkozástípusról van-e szó.

Az osztályok és a szerkezetek a .NET-ben használt közös típusú rendszer két alapvető szerkezetét képezik. Lényegében mindegyik egy adatstruktúra, amely magában foglal egy adathalmazt és a hozzá tartozó viselkedéseket, mint egy logikai egység. Az adatok és viselkedések az osztály, a struktúra vagy a rekord tagjai . A tagok magukban foglalják a metódusokat, tulajdonságokat, eseményeket stb. a cikk későbbi részében felsoroltak szerint.

Az osztály-, szerkezet- vagy rekorddeklaráció olyan, mint egy terv, amely példányok vagy objektumok futásidőben történő létrehozására szolgál. Ha osztályt, szerkezetet vagy rekordot határoz meg, PersonPerson a típus neve. Ha deklarál és inicializál egy ptípusú változót Person, akkor p nevezhető a Personobjektumának vagy példányának. Több azonos Person típusú példány hozható létre, és mindegyik példány különböző értékekkel rendelkezhet a tulajdonságaiban és mezőiben.

Az osztály egy referenciatípus. Amikor létrehoz egy ilyen típusú objektumot, az a változó, amelyhez az objektum hozzá van rendelve, csak az adott memóriára mutató hivatkozást tartalmaz. Amikor az objektumhivatkozás egy új változóhoz van rendelve, az új változó az eredeti objektumra hivatkozik. Az egyik változón végrehajtott módosítások a másik változóban is megjelennek, mivel mindkettő ugyanarra az adatra hivatkozik.

A szerkezet egy értéktípus. A szerkezet létrehozásakor az a változó, amelyhez a szerkezet hozzá van rendelve, a struktúra tényleges adatait tárolja. Amikor a szerkezet egy új változóhoz van rendelve, az át lesz másolva. Az új változó és az eredeti változó tehát ugyanazon adatok két különálló példányát tartalmazza. Az egyik példányon végrehajtott módosítások nem érintik a másik példányt.

A rekordtípusok lehetnek hivatkozástípusok (record class) vagy értéktípusok (record struct). A rekordtípusok olyan metódusokat tartalmaznak, amelyek támogatják az értékegyenlőséget.

Az osztályok általában összetettebb viselkedés modellezésére szolgálnak. Az osztályok általában az osztályobjektum létrehozása után módosítani kívánt adatokat tárolják. A szerkezetek a kis adatstruktúrákhoz ideálisak. A szerkezetek általában olyan adatokat tárolnak, amelyeket nem kívánnak módosítani a szerkezet létrehozása után. A rekordtípusok olyan adatstruktúrák, amelyek további, a fordító által szintetizált tagokkal rendelkeznek. A rekordok általában olyan adatokat tárolnak, amelyeket az objektum létrehozása után nem kívánnak módosítani.

Értéktípusok

Az értéktípusok a System.ValueType-ból származnak, amely a System.Object-ből származik. Azok a típusok, amelyek System.ValueType-ből származnak, speciális viselkedéssel rendelkeznek a CLR-ben. Az értéktípus-változók közvetlenül tartalmazzák az értékeiket. A strukturált szerkezetek memóriája a változó deklarált környezetétől függetlenül beágyazottan van lefoglalva. Az érték típusú változók esetében nincs külön halomfoglalási vagy szemétgyűjtési többletterhelés. Deklarálhat record struct olyan típusokat, amelyek értéktípusok, és tartalmazzák a rekordok szintetizált tagjait.

Az értéktípusoknak két kategóriája van: struct és enum.

A beépített numerikus típusok szerkezetek, és olyan mezőkkel és metódusokkal rendelkeznek, amelyekhez hozzáférhet:

// constant field on type byte.
byte b = byte.MaxValue;

Az értékeket azonban úgy deklarálhatja és rendelheti hozzájuk, mintha egyszerű, nem összesített típusok lennének:

byte num = 0xA;
int i = 5;
char c = 'Z';

Az értéktípusok lezárt. Nem származtathat típust egyetlen értéktípusból sem, például System.Int32. Nem definiálhat olyan strukturát, amely egy felhasználó által definiált osztályból vagy struktúraből örökölhető, mert egy struktúra csak onnan System.ValueTypeörökölhet. A szerkezetek azonban egy vagy több interfészt implementálhatnak. A szerkezettípust bármely olyan felülettípusra leadhatja, amelyet implementál. Ez az átalakítás, amely egy dobozolási művelettel burkolja be a struktúrát egy referenciatípusú objektumba a kezelt halomban. Boxing műveletek akkor fordulnak elő, ha egy értéktípust egy olyan metódusnak ad át, amely bemeneti paraméterként egy System.Object vagy bármely interfésztípust használ. További információ: Boxing and Unboxing.

A struct kulcsszóval saját egyéni értéktípusokat hozhat létre. A szerkezeteket általában tárolóként használják a kapcsolódó változók kis halmazához, ahogyan az az alábbi példában látható:

public struct Coords
{
    public int x, y;

    public Coords(int p1, int p2)
    {
        x = p1;
        y = p2;
    }
}

A szerkezetekről további információt a Struktúratípusok című témakörben talál. Az értéktípusokról további információt az Értéktípusok című témakörben talál.

Az értéktípusok másik kategóriája a enum. Az enum névvel ellátott integrál állandók készletét határozza meg. A .NET-osztálytár System.IO.FileMode enumerálása például elnevezett állandó egész számokat tartalmaz, amelyek meghatározzák a fájl megnyitásának módját. Az alábbi példában látható módon van definiálva:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

A System.IO.FileMode.Create állandó értéke 2. A név azonban sokkal kifejezőbb a forráskódot olvasó emberek számára, ezért érdemes számbavételeket használni állandó literális számok helyett. További információért lásd System.IO.FileMode.

Az összes enumeráció a System.Enum-ból származik, amely a System.ValueType-ból származik. A szerkezetekre vonatkozó szabályok az enumerálásokra is érvényesek. Az enumerálási típusokkal kapcsolatos további információkért lásd az Enumerálási típusok című témakört.

Referenciatípusok

Egy class, record, delegate tömb, vagy interface típus egy reference type.

Ha deklarál egy változót reference type, az az értéket null tartalmazza, amíg hozzá nem rendeli egy ilyen típusú példányhoz, vagy nem hoz létre egyet az new operátorral. Az osztály létrehozását és hozzárendelését az alábbi példában szemléltetjük:

MyClass myClass = new MyClass();
MyClass myClass2 = myClass;

A interface nem hozható létre közvetlenül a new operátorral. Ehelyett hozzon létre és rendeljen hozzá egy olyan osztálypéldányt, amely megvalósítja a felületet. Vegye figyelembe a következő példát:

MyClass myClass = new MyClass();

// Declare and assign using an existing value.
IMyInterface myInterface = myClass;

// Or create and assign a value in a single statement.
IMyInterface myInterface2 = new MyClass();

Az objektum létrehozásakor a memória lefoglalásra kerül a felügyelt halomban. A változó csak az objektum helyére mutató hivatkozást tartalmaz. A kezelt halomban lévő típusok többletterhet jelentenek mind a lefoglalásuk, mind a visszaigénylésük során. A szemétgyűjtés a CLR automatikus memóriakezelési funkciója, amely elvégzi a visszahívást. A szemétgyűjtés azonban rendkívül optimalizált, és a legtöbb esetben nem okoz teljesítményproblémát. A szemétgyűjtésről további információt az Automatikus memóriakezelés című témakörben talál.

Minden tömb referenciatípus, még akkor is, ha az elemei értéktípusok. A tömbök implicit módon származnak az System.Array osztályból. Deklarálhatja és használhatja őket a C# által biztosított egyszerűsített szintaxissal, ahogyan az alábbi példában látható:

// Declare and initialize an array of integers.
int[] nums = [1, 2, 3, 4, 5];

// Access an instance property of System.Array.
int len = nums.Length;

A referenciatípusok teljes mértékben támogatják az öröklést. Amikor osztályt hoz létre, örökölhet bármely más interfészt vagy osztályt, amely nincs lezártként definiálva. Más osztályok örökölhetnek az Ön osztályától, és felülbírálhatják a virtuális metódusokat. További információ a saját osztályok létrehozásáról: Osztályok, szerkezetek és rekordok. További információ az öröklésről és a virtuális módszerekről: Öröklés.

A literális értékek típusai

A C#-ban a literális értékek típust kapnak a fordítótól. A számkonstansok beírásának módját úgy adhatja meg, hogy a szám végéhez hozzáfűz egy betűt. Ha például meg szeretné adni, hogy az értéket 4.56 egyként floatkell kezelni, fűzze hozzá az "f" vagy az "F" értéket a szám után: 4.56f. Ha nem csatolnak hozzá betűt, a fordítóprogram a literál típusát automatikusan kitalálja. További információ arról, hogy mely típusokat lehet betű utótagokkal megadni: Integrál numerikus típusok és lebegőpontos numerikus típusok.

Mivel a literálok be vannak állítva, és az összes típus végső soron onnan System.Objectszármazik, írhat és fordíthat kódot, például a következő kódot:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

Általános típusok

Egy típus deklarálható egy vagy több típusparaméterrel , amelyek helyőrzőként szolgálnak a tényleges típushoz (a konkrét típushoz). Az ügyfélkód megadja a konkrét típust, amikor létrehoz egy példányt a típusból. Az ilyen típusokat általános típusoknak nevezzük. A .NET-típus System.Collections.Generic.List<T> például egy típusparamétert használ, amelyet konvenció szerint a név Tad meg. A típuspéldány létrehozásakor meg kell adnia a lista által tartalmazott objektumok típusát, például string:

List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);

A típusparaméter használata lehetővé teszi, hogy ugyanazt az osztályt újra felhasználva bármilyen típusú elemet tároljon anélkül, hogy minden elemet objektummá kellene alakítania. Az általános gyűjteményosztályokat erősen gépelt gyűjteményeknek nevezzük, mert a fordító ismeri a gyűjtemény elemeinek konkrét típusát, és fordításkor hibát okozhat, ha például egész számot próbál hozzáadni az stringList előző példában szereplő objektumhoz. További információ: Generics.

Implicit típusok, névtelen típusok és nullértékű értéktípusok

A kulcsszó használatával var implicit módon beírhat egy helyi változót (de az osztálytagokat nem). A változó a fordításkor is kap típust, de a típust a fordító adja meg. További információ: Implicit módon beírt helyi változók.

Nem célszerű névvel ellátott típust létrehozni olyan egyszerű kapcsolódó értékekhez, amelyeket nem kíván tárolni vagy átlépni a metódushatáron kívül. Ehhez névtelen típusokat hozhat létre. További információ: Névtelen típusok.

A szokásos értéktípusok nem tartalmazhatnak nullértéket. Null értékű értéktípusokat azonban a típus után hozzáfűzve ? hozhat létre. Például a(z) int? egy int típus, amely az null értékkel is rendelkezhet. A null értékű értéktípusok az általános struktúratípus System.Nullable<T>példányai. A null értékű értéktípusok különösen akkor hasznosak, ha adatokat ad át olyan adatbázisoknak, amelyekben numerikus értékek lehetnek null. További információ: Null értékű értéktípusok.

Fordítási idő típusa és futásideje típusa

A változók különböző fordítási és futásidejű típusokkal rendelkezhetnek. A fordítási idő típusa a változó deklarált vagy kikövetkezett típusa a forráskódban. A futtatási idő típusa az adott változó által hivatkozott példány típusa. Ez a két típus gyakran ugyanaz, mint az alábbi példában:

string message = "This is a string of characters";

Más esetekben a fordítási idő típusa eltérő, ahogy az alábbi két példában is látható:

object anotherMessage = "This is another string of characters";
IEnumerable<char> someCharacters = "abcdefghijklmnopqrstuvwxyz";

Mindkét fenti példában a futási idő típusa egy string. A fordítási idő típusa object az első sorban, a IEnumerable<char> másodikban van.

Ha a két típus eltér egy változótól, fontos tisztában lenni azzal, hogy mikor érvényes a fordítási idő típusa és a futásidejű típus. A fordítási idő típusa határozza meg a fordító által végrehajtott összes műveletet. Ezek a fordítóműveletek közé tartoznak a metódushívás feloldása, a túlterhelés kezelése, valamint az elérhető implicit és explicit átalakítások. A futási idő típusa határozza meg a futtatáskor feloldott összes műveletet. Ezek a futásidejű műveletek közé tartozik a virtuális metódushívások küldése, az is és switch kifejezések értékelése, valamint más típusellenőrző API-k. A kód típusok közötti interakcióinak jobb megértéséhez ismerje fel, hogy melyik művelet melyik típusra vonatkozik.

További információkért lásd a következő cikkeket:

C# nyelvspecifikáció

További információkért lásd a C# nyelvi specifikációját. A nyelvi specifikáció a C#-szintaxis és -használat végleges forrása.