Programozzunk C#-ban – 5. rész
A sorozat 5. részében a tulajdonságokkal, a tárolókkal és kódunk dokumentálásával foglalkozunk.
Tulajdonságok
A tulajdonságok C# esetén a Getter/Setter metódusokat váltják ki. Úgy kezelhetőek, mint változók, azonban függvények. A tulajdonságok alapvető használatát az alábbi kód mutatja be:
using System; namespace _10_tulajdonsag { public class Tulajdonsag { private int _ertek; public int Ertek { get { Console.WriteLine("Az erteket lekerdeztek"); return _ertek; } set { _ertek = value; Console.WriteLine("Az erteket beallitottak"); } } } class Program { static void Main(string[] args) { Tulajdonsag teszt = new Tulajdonsag(); teszt.Ertek = 55; Console.WriteLine(teszt.Ertek); } } } |
A tulajdonságok lehetnek anonim tulajdonságok is. Az anonim tulajdonságok akkor jönnek jól, ha a tulajdonságnak nem kell extra kódot futtatnia, vagyis azt szeretnénk, ha úgy viselkedne, mint egy változó.
Változók esetén azonban nem szabályozható a hozzáférés. Az alábbi példakód egy olyan tulajdonság használatát mutatja be anonim és nem anonim módon, amely csak olvasható.
using System; namespace _11_anonim_tulajdonsag { class NemAnonim { private int _ertek; public NemAnonim() { _ertek = 33; } public int Tulajdonsag { get { return _ertek; } } } public class Anonim { public int Tulajdonsag { get; private set; } public Anonim() { Tulajdonsag = 33; } } class Program { static void Main(string[] args) { NemAnonim n = new NemAnonim(); Anonim a = new Anonim(); Console.WriteLine(n.Tulajdonsag); Console.WriteLine(a.Tulajdonsag); } } } |
A tulajdonságok között különleges a tömb indexelést lehetővé tévő tulajdonság. Segítségével olyan tulajdonságot tudunk létrehozni, amely indexelhető. Ennek a használatát mutatja be az alábbi példa:
using System; namespace _12_tomb_indexer { class TombIndexer { public string this[int i] { get { switch (i) { case 0: return "Nulla"; case 1: return "Egy"; case 2: return "Kettő"; case 3: return "Három"; case 4: return "Négy"; case 5: return "Öt"; case 6: return "Hat"; default: return "Sok"; } } } } class Program { static void Main(string[] args) { TombIndexer teszt = new TombIndexer(); for (int i=0; i<7; i++) { Console.WriteLine(teszt[i]); } } } } |
Kód dokumentálása
C# esetén lehetőségünk van a nyelv beépített szolgáltatásával/elemével arra, hogy a kódunkat megfelelően dokumentáljuk. Erre a /// jel van bevezetve. A /// jel után XML dokumentáció következhet. Visual Studio esetén, ha a létrehozott metódusunkat, osztályunkat, struktúránkat, tulajdonságunk definícióját megelőző sorba 3db / jelet írunk, akkor legenerálja a szükséges XML adattagokat, amiket nekünk csak ki kell töltenünk megfelelően.
Ebből később a Visual Studio intelligens tippeket tud generálni a kód használatáról, valamint a megfelelő eszközzel remek dokumentációs weblapok rakhatóak össze ebből az információból.
A kód dokumentálása azért jó, mert ha elővesszük a régen írt programunkat bővítés, vagy módosítás céljából, akkor könnyebb kitalálni, hogy hogy is működik az adott program. Vagyis a dokumentációnak nem csak nagy projektek esetén van értelme. Mondhatni a kód méretétől függetlenül ajánlott a dokumentálás.
Tárolók
Minden érdekes program adatokkal dolgozik, amelyeket valamilyen struktúrában tárolni kell a programunkon belül. Erre szolgálnak a tároló osztályok. Tároló osztályokból alapvetően két típus létezik a .NET rendszeren belül: vannak a nem generikus és a generikus tárolók.
A nem generikus tárolók minden objektumot object típusra vezetnek vissza és ezeket tárolják. Ez a megoldás a .NET 1.0-ban mutatkozott be, korábbi Delphi/Java sémát követve.
A probléma ezzel az, hogy minden egyes használatkor az objektumokat konvertálgatni kell. Ezt szakirodalomban “Boxing and Unboxing”-nak nevezik. Ha rengeteg objektummal dolgozunk, akkor ez a konvertálgatás jelentős mennyiségű erőforrást emészt fel és végeredményben tetűlassú lesz a program futása.
Ezért a .NET 2.0 megalkotásakor a futtatókörnyezetet felkészítették a generikus programozásra. A generikus programozás hasonlít a C++ template rendszeréhez, azonban jóval kifinomultabb.
A generikus programozás alapelve az, hogy típustól függetlenül alkossuk meg az algoritmusainkat és adatszerkezet tárolóinkat. A generikus tárolók sokkal gyorsabbak, mivel nincs “Boxing and Unboxing” probléma.
A nem generikus tárolók a System.Collections névtérben helyezkednek el, míg a generikus változatok a System.Collections.Generics névtérben. A fontosabb beépített tárolók:
- List<T>
Lista adatszerkezet. Menet közben bővíthető elemekkel. Az egyes elemekre tömbszerűen, index segítségével hivatkozhatunk, vagy a foreach ciklus segítségével. Bővíteni a listát csak a végén lehet. - Dictionary<T, T2>
Szótár adatszerkezet. T típusú elemekkel indexeljük a T2 típusú elemeket. - LinkedList<T>
Kétirányú láncolt lista. Bővíthető bármelyik pontján.Szekvenciális olvasásban (elejétől a végéig) gyorsabb, mint a List, viszont ha véletlenszerűen kell ugrálni az elemek között, akkor a felépítéséből adódóan siralmasan lassú. Így általánosságban elmondható, hogy a List használata kifizetődőbb, egy-két speciális esetet leszámítva. - Queue<T>
Sor adatszerkezet. - Stack<T>
Verem adatszerkezet. - SortedSet<T>
Automatikusan sorba rendezett Lista szerű adatszerkezet. Minden egyes új elem behelyezésekor vagy elem törlésekor automatikusan sorba rendeződik. - SortedDictionary<T, T2>
T alapján sorba rendezett szótár adatszerkezet. Szintén minden egyes új elem esetén vagy törléskor automatikusan sorba rendeződik.
A tárolók esetén a jelzi a típust. Tehát ha egy szövegek tárolására alkalmas listát szeretnék létrehozni, akkor azt a következőképpen tehetem meg:
List<string> lista = new List<string>(); |
A tárolók az ICollection felületet valósítják meg, így mindegyik ugyanazokkal a főbb tulajdonságokkal és metódusokkal rendelkezik. Ezek:
- int Count
Visszaadja a tárolóban tárolt elemek számát. - void Add(T item)
Elem hozzáadása a tárolóhoz. - void Clear()
Tároló összes elemének törlése. - bool Contains(T item):
Megnézi, hogy a paraméterként megadott objektumot tartalmazza-e a tároló. - void CopyTo(T[] array, int arrayIndex):
A tároló elemeit átmásolja egy tömbbe. A második paraméter a tömbben a kezdő indexet határozza meg. - bool Remove(T item)
Törli a paraméterként megadott elemet a tárolóból. Visszatérési értéke igaz, ha a törlés sikeres volt, hamis, ha nem.
Dictionary tárolóban kulcs tulajdonságnak csak olyan objektumok használhatóak, amelyek a GetHashCode() függvényt megfelelően implementálják (Lásd előző cikk).
Foreach
A foreach ciklus a C# nyelv esetén direkt elemeket tároló objektumokban elemek kiválasztására szolgáló ciklus. Minden olyan objektumon alkalmazható, amely megvalósítja az IEnumerable felületet. A ciklus szintaxisa:
foreach (ciklus_változó in tároló) { //műveletek } |
A ciklus változó típusát célszerű a var kulcsszóval megadni, így a ciklus kód átláthatóbb lesz. Az alábbi példa a List tárolót és a foreach ciklus használatát mutatja be.
using System; using System.Collections.Generic; namespace _13_tarolok { class Program { /// <summary> /// A Program fő függvénye /// </summary> /// <param name="args">parancssori argumentumok</param> static void Main(string[] args) { //100db elemnek előfoglalunk helyet, így gyorsabb a működés később List<int> lista = new List<int>(100); VeletlenElemek(lista); foreach (var elem in lista) { Console.WriteLine(elem); } Console.ReadKey(); } /// <summary> /// 100Db véletlen számot generál /// </summary> /// <param name="lista">Egy Lista objektum, amibe az elemek kerülnek</param> static void VeletlenElemek(List<int> lista) { //véletlen számgenerátor Random r = new Random(); //100db véletlen szám generálása for (int i=0; i<100; i++) { lista.Add(r.Next()); } } } } |
A cikkhez tartozó példakódok a https://github.com/webmaster442/csharppeldak címen szerezhetőek be.