Oszd meg és szedd szét…
Hernyák Zoltán
Hosszabb programok írása esetén amennyiben a teljes kódot a Main tartalmazza, a program áttekinthetetlen lesz.
Mindenképpen javasolt a kód részekre tördelése. Ennek során olyan részeket különítünk el, amelyek önmagában értelmes részfeladatokat látnak el. E részfeladatoknak jól hangzó nevet találunk ki (pl. bekérés, feltöltés, kiírás, maximális elem megkeresése, listázás, kigyűjtés, stb.), ezeket a program külön részén, a Main fv-től elkülönítve írjuk meg.
Az ilyen, önálló feladattal és névvel ellátott, elkülönített programrészletet eljárásnak nevezzük.
Az eljárásnak
a visszatérési érték típusa kötelezően void,
van neve (azonosító),
lehetnek paraméterei,
van törzse.
Pl:
static void Információ()
{
Console.WriteLine("*******************");
Console.WriteLine("** Példa program **");
Console.WriteLine("Programozó: Kiss Aladár Béla");
Console.WriteLine("Készült: 2004.11.23");
}
A fenti példában az eljárás neve ’Információ’, és saját utasításblokkja van (a ’{’ és ’}’ közötti rész). Az eljárás (egyelőre) ’static void’ legyen, és a zárójelpár megadása kötelező abban az esetben is, amennyiben nincsenek paraméterek.
Amennyiben a program adott pontján szeretnénk végre hajtani az eljárásban foglalt utasítássorozatot, úgy egyszerűen hivatkoznunk kell rá a nevével (eljárás indítása, eljárás meghívása):
static void Main(string[] args)
{
Információ();
}
Ekkor az adott ponton az eljárás végrehajtásra kerül, majd a végrehajtás visszatér a hívást követő utasításra, és folytatódik tovább a program futása:
Természetesen van rá lehetőség, hogy ne csak a Main()-ből hívjunk eljárásokat, hanem az eljárásokból is van lehetőség további eljárásokat hívni:
C#-ban az eljárások neve azonosító, ezért érvényes az azonosító névképzési szabálya: betűvel vagy aláhúzással kezdődik, betűvel, számjeggyel, aláhúzás karakterekkel folytatódhat (de nem tartalmazhat szóközt)! A C#-ban szabad ékezetes karaktereket is használni az eljárás-nevekben, de nem javasolt – karakterkészlet és kódlap függő forráskódot eredményez, e miatt másik gépen másik programozó által nehezen elolvashatóvá válik a forráskód.
Hová írjuk az eljárásainkat?
Az Objektum Orientált programozás elvei szerint a programok építőkövei az osztályok. Az osztály (class) mintegy csoportba foglalja az egy témakörbe, feladatkörbe tartozó eljárásokat, függvényeket, változókat. E miatt a program felépítése a következő lehet:
using System;
namespace Tetszoleges_Nev
{
class PeldaProgram
{
Más eljárások ...
static void Main(string[] args)
{
...
}
További eljárások ...
}
}
Az egyéb eljárásainkat ugyanabba a csoportba (osztályba) kell megírni, mint ahol a Main() fv található. A sorrendjük azonban lényegtelen, mindegy, hogy a saját eljárásainkat a Main() fv előtt, vagy után írjuk le, illetve írhatjuk őket vegyes sorrendben is. Az eljárások egymás közötti sorrendje sem lényeges. Ami fontos, hogy közös osztályba (class) kerüljenek.
A programok eljárásokra tagolása persze újabb problémákat szül. Az eljárások ’nem látják’ egymás változóit. Ennek oka a változók hatáskörének problémakörében keresendő.
Minden változónévhez tartozik egy hatáskör. A hatáskör megadja, hogy az adott változót a program szövegében mely részeken lehet ’látni’ (lehet rá hivatkozni). A szabály az, hogy a változók csak az őket tartalmazó blokk-on belül ’láthatók’, vagyis a változók hatásköre az őket tartalmazó blokkra terjed ki.
static void Main(string[] args)
{
int a=0;
Feltoltes();
Console.WriteLine("{0}",a);
}
static void Feltoltes()
{
a=10;
}
A fenti kód hibás, mivel az ’a’ változó hatásköre a ’Main’ függvény belseje, az ’a’ változóra a ’Feltoltes()’ eljárásban nem hivatkozhatunk. Ha mégis megpróbáljuk, akkor a C# fordító a The name 'a' does not exist … kezdetű fordítási hibaüzenettel jelzi ezt nekünk.
Ezt (hibásan) az alábbi módon reagálhatjuk le:
static void Main(string[] args)
{
int a=0;
Feltoltes();
Console.WriteLine("{0}",a);
}
static void Feltoltes()
{
int a=10;
}
Ekkor a ’Feltoltes()’ eljárásban is létrehozunk egy másik, szintén ’a’ nevű változót. Ez a változó sajátja lesz a ’Feltoltes()’-nek, de semmilyen kapcsolatban nem áll a ’Main()’ függvény-beli ’a’ változóval. A nevek azonossága ellenére ez két különböző változó, két különböző adatterületen helyezkedik el. Ezért hiába tesszük bele az ’a’ változóba a ’10’ értéket, ezen érték a ’Feltoltes()’ eljárásbeli ’a’ változóba kerül bele, nem a Main()-beli ’a’ változóba.
Hogyan kell megosztani változókat eljárások között?
Mivel a hatáskör-szabály szigorú, és nincs kivétel, ezért mindaddig, amíg az ’a’ változót a Main() függvény blokkjában deklaráljuk, addig e változót más eljárásokban nem érhetjük el. A megoldás: ne itt deklaráljuk!
class PeldaProgram
{
static int a=0;
static void Main(string[] args)
{
Feltoltes();
Console.WriteLine("{0}",a);
}
static void Feltoltes()
{
a=10;
}
}
Azokat a változókat, amelyeket szeretnénk megosztani az eljárásaink között, azokat az eljárásokat összefogó osztály (class) belsejében, de az eljárásokon kívül kell deklarálni. Mivel az eljárásaink static jelzővel vannak ellátva, ezért a közöttük megosztott változókat is ugyanúgy static jelzővel kell ellátni. Ha ezt elmulasztanánk, akkor a static eljárások nem ’látnák’ a nem static változókat.
Nézzünk egy összetett feladatot: kérjünk be egy tíz elemű vektor elemeit billentyűzetről, majd írjuk ki a vektor elemeinek értékét a képernyőre egy sorban egymás mellé vesszővel elválasztva, majd számoljuk ki és írjuk ki a képernyőre a vektor elemeinek összegét.
Figyeljük meg, hogy a Main() függvény nem tartalmaz lényegében semmilyen kódot, a tényleges műveleteket eljárásokba szerveztük, és jól csengő nevet adtunk nekik:
class PeldaProgram
{
static int[] tomb=new int[10];
static int osszeg=0;
//..................................................
static void Main(string[] args)
{
Feltoltes();
Kiiras();
OsszegSzamitas();
Console.WriteLine("A tomb elemek osszege={0}"
,osszeg);
}
//..................................................
static void Feltoltes()
{
for(int i=0;i<tomb.Length;i++)
{
Console.Write("A tomb {0}. eleme=",i);
string strErtek=Console.ReadLine();
tomb[i] = Int32.Parse( strErtek );
}
}
//.................................................
static void Kiiras()
{
Console.Write("A tomb elemei: ");
for(int i=0;i<tomb.Length;i++)
Console.Write("{0}, ",tomb[i]);
Console.WriteLine();
}
//.................................................
static void OsszegSzamitas()
{
osszeg=0;
for(int i=0;i<tomb.Length;i++)
osszeg = osszeg + tomb[i];
}
//..................................................
}
A változók között meg kellett osztani magát a tömböt, mert azt minden eljárás használta. Ezen túl meg kellett osztani egy ’osszeg’ nevű változót is, mert ezen változó értékét az ’OsszegSzamitas()’ eljárás számolja ki, és a Main() függvény írja ki a képernyőre.