Felhasználó által definiált típusok
Eddig megtanultunk olyan alapvető, primitív típusokat, mint az int
, string
, float
vagy a bool
. Ezek és néhány egyéb típus beépítetten a C# nyelv része és nehéz elképzelni szoftvert, ami ne használná őket. Viszont ahogy a legtöbb programozási nyelvben, így a C#-ban is lehetőségünk van programozóként új típusokat definiálni.
Az eddig írt programjaink mindig egy konkrét fájlba történtek. Ezzel szemben egy kódbázis rendszerint felbomlik sok fájlra.
Egy C# kódbázisban csakis egy file tartalmazhat legfelső szintű utasításokat (Top Level Statements). Ez a fájl mindig a program belépési pontja. Minden egyéb programkód csak típusdefiníciókon belül létezhet. (Ennek módját később fogjuk látni.)
Típusdefinícióknak mindig a kiinduló fájlban a legfelső szintű utasítások után vagy pedig teljesen külön fájlba kell írnunk.
A legegyszerűbb felhasználó által definiált típus az enum, amit más néven felsorolt típusnak is neveznek.
Egy enum definíciója nem több, csupán a felsorolása a lehetséges értékeknek, amiket az adott típusú enum objektum felvehet.
Pl. képzeljünk el egy típust, ami a következőkre vehet fel értékeket:
- Hét napjai: Hétfő, Kedd, Szerda, Csütörtök, Péntek, Szombat, Vasárnap
- Égtájak: Észak, Kelet, Dél, Nyugat
- Az őselemek a játékunkban: Tűz, Víz, Föld, Levegő
Az enum-okat definiálni kell, mielőtt példányokat hoznánk létre belőle:
enum DayInAWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
enum GeneralDirection { North, East, South, West }
enum Element { Fire, Water, Earth, Air }
Az enum-okra a típusukkal és értékükkel együtt lehet hivatkozni.
DayInAWeek day = DayInAWeek.Wednesday;
Console.WriteLine("$It is {day}, my dudes."); // Unity-ben: Debug.Log(...)
Element element = Element.Water;
Íme egy példa függvény, ami visszaadja, hogy ki nyert egy kő/papír/olló játékban.
enum Choices { Rock, Paper, Scissors } // 3 Elemű enum definiálása
enum Result { Win, Lose, Draw } // 3 Elemű enum definiálása
Result PlayRockPaperScissors(Choices yourChoice, Choices enemiesChoices)
{
// Döntetlen
if(yourChoice == enemiesChoices)
return Result.Draw;
// Győzelem
if(yourChoice == Choices.Rock && enemiesChoices == Choices.Scissors)
return Result.Win;
if(yourChoice == Choices.Paper && enemiesChoices == Choices.Rock)
return Result.Win;
if(yourChoice == Choices.Scissors && enemiesChoices == Choices.Paper)
return Result.Win;
// Vereség
return Result.Lose;
}
Játék tutorial: Kő-Papír-Olló Háború
Típusbiztonság
Felmerülhet a kérdés: Miért kell nekünk új típus mindezekre?
Nem lehetséges, hogy például a hét napjai esetén megfeleltetek minden napot egy int
számnak a 0-tól indulva 6-ig és ebben tárolom az értékeket, ahelyett, hogy egy új típust hoznék létre a célra.
Ennek technikailag semmi akadálya nincs megtenni. Sőt a C# a háttérben pontosan ugyanígy működik, ám nem javasolt.
Mit veszítünk, ha int
-ben tárolunk valamit, ami csak fix számú konkrét értéket vehet fel:
- Olvashatatlanabb lesz a kód: Mindenkinek fejből kell tudnia melyik szám mit jelent.
- Kevésbé lesz biztonságos a kód: Véletlenül kerülhetnek olyan értékek is egy változóba, ami nincs értelmezve.
int dayInAWeek = 33; // Error
Mindazonáltal nem feltétlenül érdemes mindig enumot lérehozni. Néha ez csak felesleges komplikáció. Minden programozói eszközt lehetséges túlhasználni. Ez az enum-okra is igaz. Csak akkor javasolt egy új típust definiálni, ha úgy érezzük ez segíti a kód átláthatóságát és nem pedig rombolja azt. A hatodik érzékünk erre hamarosan ki fog alakulni.
Castolás
Előbb említettük, hogy a háttérben az enum is csak egy int szám, így explicit kasztolással könnyedén átalakítható a két típus. Erre szükségünk is lehet sok esetben, például ha matematikai műveleteket szeretnénk végezni az adaton:
enum DayInAWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
// Ez a függvénny megmondja a hét egy napjáról hogy melyik a következő
DayInAWeek NextDay(DayInAWeek day)
{
int dayIndex = (int)day; // Kasztolás int-be
dayIndex++; // Megnövelem, az intet egy-gyel
dayIndex %= 7; // Ha túlléptük a vasárnapot, indul a hét előlről
return (DayInAWeek)dayIndex; // Kasztolás vissza enum-ba
}
Alapesetben az enumok számozása 0-tól kezdődik egyenként felefelé, de arra is van lehetőség, hogy manuálisan definiáljuk egy enum minden egyes értékének int verzióját:
enum DayInAWeek {
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7}
Összes enum
Gyakran szükség lehet egy enum típus minden elemére egy tömbben. Ezt a következő módon tudjuk lekérni:
// Az összes elem lekérdezése:
Element[] allElements = (Element[])System.Enum.GetValues(typeof(Element));
foreach(Element element in allElements ) // Az összes elem kiíratása
{
Console.WriteLine(element); // Parancssori általános kiíratás
Debug.Log(element); // Kíratás Unity konzolra
}
Kiegészítés:
Enum-ok ilyen tömbjét érdemes egyszer lekérni és elmenteni majd később erre az egy elmentett listára hivatkozni csak, hiszen így nem kell minden egyes használat esetén összeállítani egy tömböt ezáltal fölösleges számításokat végezni, fölösleges memóriát foglalni. Ennek módját a lenti kód demonstrálja, aminek megértéséhez olvasd el az összetett típusokkal és a statikusokkal foglalkozó fejezeteket:
static class ElementHelper
{
public static readonly Element[] allElements =
(Element[])System.Enum.GetValues(typeof(Element));
}