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.

code

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.