A .NET ugyan rendelkezik 8 és 16 bites egész tÃpusokkal, de műveleteket csak 32 és 64 bites egész számok között tud natÃvan elvégezni.
Ennek oka a processzor és a JIT működésében keresendő. Az x86/x64 processzorok rendelkeznek egy védett üzemmóddal. Ez teszi lehetővé a modern operációs rendszerekben a folyamatok közötti izolációt. Ennek egyik mellékhatása, hogy ha a processzor védett, 32 bites üzemmódban fut, akkor a 16 bites üzemmódra átállás egy kontextus váltással jár számára, ami költséges. Továbbá ha 64 bites üzemmódban fut, akkor direktben nem is tud 16 bitre visszaváltani.
Ennek következménye, hogy a 8 és 16 bites egész tÃpusokkal végzett aritmetikai műveletek valójában 32 bites egészként lesznek elvégezve.
Ez a gyakorlatban azt jelenti, hogy ha short, ushort, byte vagy sbyte műveleteken végzünk eredményt, akkor azok eredménye minden esetben egy int lesz. Nézzünk egy példát:
byte a = 100;
byte b = 100;
//fordÃtási hiba, mivel az `int` nem implicit átalakÃtható byte-ra
byte c = a * b;
A működés a fenti példában egyértelmű, sőt az alábbiban is az, de megtévesztő tud lenni:
int a2 = 100000;
int b2 = 100000;
long c2 = a2 * b2;
Ebben az esetben azt várnánk, hogy a c2 változó értéke 10 000 000 000 legyen. Azonban 1 410 065 408 lesz a c2 változó értéke. Ez annak köszönhető, hogy a2 és b2 is 32 bites egészek, ami azt vonzza magával, hogy 32 bites aritmetika alapján lesznek összeszorozva, ami túlcsordul.
Az utóbbi kódrészlet javÃtása egyszerű. Elég az egyik változót long tÃpusra konvertálni és akkor a művelet 64 bites aritmetikával lesz végrehajtva, ami már a helyes eredményt adja:
int a2 = 100000;
int b2 = 100000;
long c2 = (long)a2 * b2;
Összegezve tehát a short, ushort, byte vagy sbyte tÃpusok aritmetikai használata elÅ‘tt mindig történni fog a háttérben egy implicit int tÃpusra konvertálás, mÃg int tÃpusról long tÃpusra ez nem történik meg automatikusan, még akkor sem, ha a cél változó, amiben az eredményt tárolnánk long tÃpusú.
Ennek a működésnek az az oka, hogy a .NET támogat x86 és 32 bites ARM platformokat is, ahol szintén elvégezhetÅ‘nek kell lennie két egész szorzásának, összeadásának. Ha minden aritmetikai művelet esetén megkövetelnénk, hogy 64 bites legyen a kimenet, akkor az olyan rendszereken, ahol nem 64 bit natÃvan az aritmetika felépÃtése, ott jóval lassabban történne meg minden művelet, illetve logikátlan is lenne, ha már egy 1+1 eredménye is long lenne.
Amennyiben fontos, hogy az egész számainkon végzett műveleteink ne járjanak túlcsordulással, akkor a műveleteket végezzük el checked kontextusban:
checked
{
int a2 = 100000;
int b2 = 100000;
long c2 = a2 * b2;
}
Ha Ãgy futtatjuk a kódot, akkor futásidÅ‘ben kivétel keletkezik, amit le tudunk kezelni megfelelÅ‘en. A kivételkezelésrÅ‘l (try-catch) és a checked kontextusról a következÅ‘ fejezetben lesz részletesen szó.