A C# erősen típusos nyelv, ebből adódóan minden változónak meg kell adnunk a típusát. Ebben a fejezetben a C# nyelv alap típusait tekintjük át. Az összes .NET programozási nyelv architektúra független típus rendszerrel rendelkezik. Ez azt jelenti, hogy egy adott típusú változó mindig ugyan úgy kerül ábrázolásra a memóriában, attól függetlenül, hogy milyen fizikai gépen is fut a program.
Mivel a nyelv teljesen objektumorientált, így az alapvető típusok is objektumok. Ez számos kiegészítő funkcionalitás létrehozására ad lehetőséget. Az alábbi táblázat a C# típusok fontosabb tulajdonságait foglalja össze:
| Típus név | Bitek száma / byte | .NET típus | Minimum érték | Maximum érték |
|---|---|---|---|---|
| byte | 8 / 1 | System.Byte | 0 | 28 -1 |
| sbyte | 8 / 1 | System.SByte | -27 | 27 -1 |
| short | 16 / 2 | System.Int16 | -215 | 215 -1 |
| ushort | 16 / 2 | System.UInt16 | 0 | 216 -1 |
| int | 32 / 4 | System.Int32 | -231 | 231 -1 |
| uint | 32 / 4 | System.UInt32 | 0 | 232 -1 |
| long | 64 / 8 | System.Int64 | -263 | 263 -1 |
| ulong | 64 / 8 | System.UInt64 | 0 | 264 -1 |
| Int128 | 128 / 16 | System.Int128 | -2127 | 2127 – 1 |
| UInt128 | 128 / 16 | System.UInt128 | 0 | 2128 |
| float | 32 / 4 | System.Single | 1,175494351 x 10–38 | 3,402823466 x 1038 |
| double | 64 / 8 | System.Double | -2,2250738585072014 x 10-308 | 1.7976931348623158 x 10308 |
| Half | 16 / 2 | System.Half | -65504 | 65504 |
| decimal | 128 / 16 | System.Decimal | -7.9 x 1028 | 7.9 x 1028 |
| boolean | 8 / 1 | System.Boolean | false | true |
| string | 16 / 2 (karakterenként) | System.String | 0 karakter | 232-1 karakter |
| char | 2 byte | System.Char | 0 (uint-re konvertálva) | 216 -1 (uint-re konvertálva) |
| var | – | – | – | – |
| dynamic | – | – | – | – |
| object | – | – | – | – |
Egész számok
A pozitív egész számok felépítésében nincsen semmi speciális. Azonban, ha a számunk negatív szám, akkor már van. Ábrázolhatunk egy számot úgy, hogy a legnagyobb helyi értéken lévő bitet a bal oldalinak tekintjük, vagy akár lehet a legnagyobb helyi értéken lévő szám a jobb oldalon is. Ezt a két ábrázolási módot Big Endian és Little Endian kódolásnak nevezik.
Számítógépek esetén mindkettő használatos, CPU-tól függ. Az Intel x86 és x64 kompatibilis processzorok mindegyike a Little Endian sémát követi, míg a telefonokban használatos ARM és egyéb RISC processzorok meg a Big Endian elvet követik.
A legnagyobb helyi értéken lévő bitet angolul Most Significant Bitnek szokás nevezni (rövidítve MSB), míg a legkisebb helyi értéken lévőt Least Significant Bitnek (rövidítve LSB).
Negatív számok bináris számrendszerben csak úgy ábrázolhatóak, hogy egy bitet előjelnek használunk fel. Ez a bit a legnagyobb helyi értéken lévő bit szokott lenni. Ha ennek az értéke nulla, akkor a szám pozitív szám, ha az értéke egy, akkor a szám negatív. További módosítás a negatív számoknál a komplemens kódolás alkalmazása. Ebből megkülönböztetünk egyest és kettest.
A komplemens szó kiegészítőt jelent. Jelen esetben azt a számot, amely kiegészíti az eredeti számunkat az n biten ábrázolható legnagyobb számra. Gyakorlatban ezt úgy szokás elvégezni, hogy a szám összes bitjét negáljuk. Például a -65 szám egyes komplemensben ábrázolva 8 biten:
Egy adott n biten felírható szám kettes komplemens kódja alatt azt a számot értjük, amely a kérdéses számot kiegészíti az n biten felírható legnagyobb számnál eggyel nagyobb számra. Ennek az előállítása úgy történik, hogy a szám összes bitjét negáljuk, majd hozzáadunk egyet. Például a -65 szám kettes komplemensben ábrázolva 8 biten:
A komplemens kódokat azért találták ki, hogy egyszerűbb legyen az aritmetikai egységek felépítése. Ha egyes komplemens kódban adunk össze két számot, akkor előjelhelyes eredményt fogunk kapni. A kettes komplemens kódot azért alkalmazzák, mert így egyszerűbb a túlcsordulások (bit átvitelek) kezelése, és lényegében így a kivonás művelet visszavezethető összeadásra.1
C# esetén, ha aritmetikai műveletet végzünk kettő, az int típusnál kevesebb bitszámmal rendelkező változón, akkor az eredmény mindig Int lesz. Ennek két fő oka van. Az első ok az optimalizáció, a második ok pedig a memória menedzser. A memória menedzser nem engedi az értékek túlcsordulását.2
A túlcsordulás védelem egy speciális kód blokk segítségével kikapcsolható, azonban ezen blokk használata nem biztonságos kódot eredményezhet. Ezen nem biztonságos blokk használatának akkor lehet értelme, ha sok műveletet kell végeznünk és gyorsítani szeretnénk a kódunk működését. Az extra túlcsordulás védelem lassíthatja a műveletek elvégzését.
Ezen speciális vezérlési blokk használatáról a későbbiekben lesz szó.
Az Int128 és UInt128 típusok nem rendelkeznek natív C# típus kulcsszóval, mivel ezen típusok relatíve újak, a .NET 7 óta elérhetőek.
Lebegőpontos számok
A lebegőpontos számok ábrázolásának módja az IEEE754-es szabvány alatt lett definiálva 1985-ben. A szabványt 2008-ban felülvizsgálták és némileg kibővítették. A mostani használatos szabvány hivatalos neve az IEEE754-2008.
A fix bithosszúságú lebegőpontos számábrázolás matematikai alapja az, hogy minden bináris szám felírható a következő alakban:
A képletben M az előjeles mantisszát3 jelöli, k pedig az előjeles karakterisztikát4. Így lényegében a számunk két szám ábrázolásából áll elő. Ezen módszer előnye, hogy a tizedespont vándorolhat és általa a pontosság is változó tud lenni. Ez néhány esetben pozitívum, míg néhányban negatívum. Ezen ábrázolási móddal n bit esetén 2n-4 lebegőpontos számot tudunk ábrázolni pontosan. Mivel végtelen sok lebegőpontos szám létezik, ezért előbb-utóbb kerekítési hibába fogunk botlani.
A különböző IEEE754-2008 szabványú számformátumok között a különbség nem csak a legnagyobb és legkisebb ábrázolható számban van, hanem a pontosságban is. Minél nagyobb méretű számokkal dolgozunk, annál kisebb a kerekítési hiba valószínűsége. A definiált formátumok a következőek:
| Típus | Karakterisztika | Mantissza | Ʃ bitszám | Karakterisztika eltolása | Pontosság bitekben | Pontosság tizedesjegyekben |
|---|---|---|---|---|---|---|
| Half | 5 | 10 | 16 | 15 | 11 | ~3,3 |
| Single | 8 | 23 | 32 | 127 | 24 | ~7,2 |
| Double | 11 | 52 | 64 | 1023 | 53 | ~15,9 |
| Double Ext. | 15 | 64 | 80 | 16383 | 64 | ~19,2 |
| Quad | 15 | 112 | 127 | 16383 | 113 | ~34,0 |
A definiált formátumok mindegyikének bitsorrendje a következő: előjelbit (1), karakterisztika bitek (5,8,11 vagy 15), mantissza bitek (10, 23, 52, 64, vagy 112).
A szabvány definiál négy darab számértéket, amelyek kiesnek az ábrázolható számtartományból. Ezek a speciális számok:
-
Nulla
Nulla ábrázolására fenntartott kód
-
Pozitív végtelen
Akkor keletkezik, ha a szám nem negatív és túlmutat a formátum ábrázolási tartományán
-
Negatív végtelen
Akkor keletkezik, ha a szám negatív és kisebb a legkisebb ábrázolható számnál a kiválasztott formátumban
-
NaN – nem szám
Akkor keletkezik, ha matematikailag értelmetlen műveletet próbálunk végrehajtani a számon. Pl: végtelennel osztás, nullával osztás, stb…
C# esetén a Single és Double számformátum használatára van lehetőségünk. Ezen számformátumok kezeléséért a CPU speciális részegysége, a lebegőpontos egység felelős. Ennek a belső felépítése 64 bites, így a Double számformátum használata nem okoz a programjaink során lassulást. A kerekítési hibák a számábrázolás jellege miatt következnek be. Vannak olyan tizedes törtek, amelyek kettes számrendszerben nem ábrázolhatóak tökéletesen, illetve csak igen sok biten lehetne őket pontosan ábrázolni. Ezért rendelkezik a C# egy speciális típussal, ami a Decimal nevet kapta.
Half
A Half típus a .NET 5.0 része a keretrendszernek, saját C# kulcsszót nem kapott. A típus nevével hivatkozhatunk rá. Kódírás közben explicit konverzióval kaphat értéket double vagy float konstansokból. A típus kifejezetten mesterséges inteligencia és fuzzy döntésrendszerek5 hatékony implementálása miatt került be a keretrendszerbe.
Decimal
A Decimal típus a kerekítési hibák leküzdése miatt került be a keretrendszerbe. A legtöbb tudományos alkalmazáshoz megfelel a double és a float típusok pontossága, azonban pénzügyi szoftverek esetén, ahol minden apró kerekítési hiba mérvadó lehet, érdemes decimal változó típust használni.
Offset binary format
A számítógépünk fel van készítve a kettes komplemens hardveres alkalmazására és az ezzel történő műveletvégzésre, azonban a külvilágból érkező jelekről ez nem minden esetben mondható el. Például akkor, ha egy A/D jelét kell feldolgozni.
Az analóg-digitális átalakítók viszonylag egyszerű eszközök (programozói szemszögből nézve): Egy adott referencia feszültséghez képest megmondják, hogy a rákapcsolt feszültség mekkora nagyságot reprezentál. Ezt a nagyságot ők pozitív, előjel nélküli szám tartományban tudják megtenni. Tehát ha valamihez képest negatív előjelet is szeretnénk, akkor a nulla pontot el kell tolni. Ez konkrétan azt jelenti, hogy ha van egy A/D átalakítónk ami 0 és 5V között tud mérni 4 bit felbontással, akkor ha a 2,5V-ra helyezzük a 0 pontot mind elektronikailag, mind kódban, akkor tudunk ehhez a ponthoz képest negatív értékeket is mérni. A gond az, hogy a kimeneti 4 bit még mindig előjel nélküli lesz és valójában a 0 kód a -8-at fogja reprezentálni, míg a 15-ös kód 7-et.
Ezt a fajta számábrázolást nevezzük Offset binary formatnak, vagy K többletes kódnak. Ez a fajta számábrázolás nem szabványosított semmilyen formában, így az n biten alkalmazandó K bit eltolás mértéke sincs, de általában a K = 2n-1 eltolás a legnépszerűbb, mint ahogy a fenti példában is láthattuk. Ennek az oka az, hogy ebben az esetben kettes komplemenses valódi előjeles számra konvertálni a legnagyobb helyiértéken lévő bit negálásával lehetséges. Az alábbi táblázat ezt mutatja be 3 bit esetén:
| Decimális | Offset K = 4 | 2-es komplemens |
|---|---|---|
| 3 | 111 | 011 |
| 2 | 110 | 010 |
| 1 | 101 | 001 |
| 0 | 100 | 000 |
| -1 | 010 | 110 |
| -2 | 001 | 101 |
| -3 | 000 | 100 |
Karakterek
A .NET belső működésében minden karakter UTF-16 kódolással tárolódik. Ez a kódolási mechanizmus a korábban használt ASCII szabványt váltja le.
Az ASCII kód egy karakterkódolási szabvány, amit 1963-ban dolgoztak ki annak érdekében, hogy a különböző számítógépek azonos kódokkal jelöljenek betűket. Eredetileg egy 7 bites kód, ami az angol ABC betűit és néhány írásjelét tartalmazza. Az első 32 karakter nem nyomtatható vezérlőkarakter, amelyek olyan gombokat reprezentálnak, mint a TAB, és DEL. Eredetileg a vezérlőkarakterek a kor modemei számára kerültek bele a szabványba.
A szabványt később felbővítették 8 bitesre, ami plusz 127 karakterrel egészítette ki a szabványt. A plusz 127 kód jelentése kódtáblákkal módosítható, így megoldható a különböző kultúrák írásjeleinek támogatása. Később ezeket is szabványosították, azonban ez nem oldotta meg az ASCII legnagyobb problémáját.
A gond abból fakadt, hogy az eltérő kódlapok más és más karaktereket rendeltek egy adott kódhoz. Ezért, ha a szöveg eredeti kódlapja nem volt ismert, akkor igencsak meg tudta nehezíteni a szöveg elolvasását. Az internet térhódításával a problémát ideiglenesen úgy próbálták megoldani, hogy nem használtak ékezetes betűket a kommunikáció során. Végérvényesen a problémát az UTF kódolások elterjedése oldotta meg a 2000-es évek elején.
A kódlapok problémáját az UTF úgy oldotta meg, hogy eredetileg 16 bitet alkalmazott egy karakter kódolására. Ez a lehetséges karakterek számát 256-ról 65536-ra növelte. Ma ez a kódolás UTF-16 néven ismert. Azonban mivel ennél jóval több írásjel használt a földön, a kódolást tovább kellett fejleszteni. Ennek köszönhetően mára az UTF kódolás 32 bites, ami elvben több, mint 4 milliárd írásjel kódolására alkalmas. A legfrissebb szabvány szerint ebből nagyjából csak 100 ezer írásjelet határoz meg. Így nem valószínű, hogy a kódolásban használt biteket egyhamar megnövelik.
Kompatibilitási okok miatt az UTF első 127 karaktere megegyezik az ASCII kódtáblázattal, azonban UTF esetén a vezérlő jelek nem használtak.
A kódolásnak három típusa terjedt el: UTF-16, UTF-32 és UTF-8. A kötőjel utáni szám a bitek számát jelenti. Unicode és UTF alatt külön bitszám jelzés nélkül a 32 bites változat értendő.
A 8 bites UTF változat a legelterjedtebb. Ez a kódolás változó bithosszúsággal jelöli a karaktereket. 8 bitet alkalmaz egy karakter kódolására, ha a karakter az ASCII kódtáblázatban is megtalálható, 16 bitet, ha a 16 bit elegendő az ábrázoláshoz és 32 bitet, ha csak 32 biten ábrázolható a karakter.
A számítógépes programok többsége képes automatikusan felismerni a karakterkódolást, azonban a szabványba bevezettek egy bájt sorrend jelzést, amit angol mozaik szóval BOM-nak neveznek. Ez 3 byte a fájl elején, ami a használt program számára azt jelzi, hogy a fájl UTF kódolású.
Szövegek
C# esetén speciális típus a szöveg, amely belsőleg karakterek tömbjeként tárolódik. A tömbökről a későbbiek folyamán lesz szó, ott részletesen foglalkozunk majd a szöveg típussal is.
Logikai típus
C# esetén a logika jelzésére egy speciális típust vezettek be, ami a boolean nevet kapta, a Boole algebra megalkotója George Boole után. A különlegessége ezen típusnak, hogy számokból implicit6 módon nem hozható létre, csak explicit7 típuskonverzióval.
var típus
C# esetén a var kulcsszó nem egy valódi típust jelöl. A kulcsszó a .NET 3.0-ban lett bevezetve.
Var használata esetén a fordítóra bízzuk a változó típusának meghatározását. A var használata különösen akkor jön jól, ha hosszú típusnevekkel dolgozunk és nem akarjuk kiírni, gyorsítani szeretnénk a munkánkat.
Minden var kulcsszó típusa fordítási időben meghatározottá válik, tehát nem dinamikus, menet közben típust váltó típusról beszélünk. Mivel a var típus egy értékadási kifejezés jobb oldala által lesz meghatározva, ezért inicializáció8 nélkül nem hozhatunk létre var típusú változót.
Ezt a kulcsszót osztályban elhelyezkedő tag változó definiálására nem használhatjuk, ott minden esetben egyértelműen kifejezve kell jelölni annak a típusát.
dynamic típus
A Dynamic típus a .NET 4.0 változatában mutatkozott be. Ez egy olyan változó típus, ami futás közben kap típust. A dinamikus típusos nyelvek integrálása miatt került be a keretrendszerbe. Használata minden olyan esetben kerülendő, ahol nincs rá szükség, mivel drámaian lassítja a programok működését.
-
Az egyes komplemens esetén is már összeadással valósítjuk meg a kivonást.↩
-
Túlcsordulásról akkor beszélünk, amikor a művelet eredménye túlmutat a számábrázolási tartományon. Például ez akkor következhet be, ha van egy byte típusú változónk, ami 255-ös értékkel rendelkezik. Ehhez ha hozzá adunk egyet, akkor 256-ot kapunk, ami már 9 biten lenne ábrázolható, de mivel csak 8 bitünk van, csak az alsó 8 bit marad meg, ami csupa nulla lesz.↩
-
Egészre normált törtszám, melynek első jegye mindig 1. Ezt a bitet a formátum nem tárolja, csak a törtrészt.↩
-
Ez jelöli ki a számban a kettedes pont helyét. A karakterisztikát eltolt nullpontú formában szokás tárolni. Ha a karakterisztika mező hossza k bit, akkor az eltolási érték e = 2k-1-1↩
-
"Az elmosódott halmazok logikája (angolul: fuzzy logic) a többértékű logikai szemantikák egyike. Tulajdonképpen fuzzy logika név alatt egy egész elméletcsaládról beszélhetünk, melynek sokrétű alkalmazásai vannak elsősorban az informatikában" – https://hu.wikipedia.org/wiki/Elmos%C3%B3dott_halmazok_logik%C3%A1ja↩
-
Rejtett, közvetett↩
-
Világosan kimondott, egyértelmű↩
-
Kezdő érték↩