Programozzunk C#-ban – 6. rész

A 6. része a sorozatnak egy viszonylag nagyobb alkalmazás elkészítésével foglalkozik. A készített alkalmazás egy pénz címletező program lesz.

A program konzolos, grafikus felülettel nem rendelkezik. A program forráskódja a következő:

using System;
using System.Collections.Generic;
 
namespace _14_cimletezo
{
 
    /// <summary>
    /// Kiegészítő függvények
    /// </summary>
    static class Kiegeszitok
    {
        /// <summary>
        /// Szótár objektumhoz fűz hozzá egy elemet. Ha az elem létezik a megadott kulccsal, akkor értéket cserél
        /// </summary>
        /// <typeparam name="TKey">A kulcs típusa</typeparam>
        /// <typeparam name="TValue">Az érték típusa</typeparam>
        /// <param name="dictionary">A szótár</param>
        /// <param name="key">A kulcs</param>
        /// <param name="value">Az érték</param>
        public static void AddEditItem<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
        {
            if (dictionary.ContainsKey(key)) dictionary[key] = value;
            else dictionary.Add(key, value);
        }
    }
 
    class Program
    {
        /// <summary>
        /// Kerekítés szabályainak megfelelően kerekíti a bemeneti összeget
        /// </summary>
        /// <param name="bemenet">Bemeneti összeg</param>
        /// <returns>Kerekített kimenet</returns>
        private static long kerekit(long bemenet)
        {
            long kimenet = bemenet;
            long maradek = bemenet % 10;
            if (maradek >= 8) kimenet += (10 - maradek);
            else if (maradek >= 6 && maradek <= 7) kimenet -= (maradek - 5);
            else if (maradek >= 3 && maradek <= 4) kimenet += (5 - maradek);
            else if (maradek >= 1 && maradek <= 2) kimenet -= maradek;
            return kimenet;
        }
 
        /// <summary>
        /// A megadott összeget címletekre bontja fel
        /// </summary>
        /// <param name="osszeg">a felbontandó összeg</param>
        private static void Cimletez(long osszeg)
        {
            long hatravan = osszeg;
            int[] cimletek = { 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5 };
            Dictionary<int, long> cimletezett = new Dictionary<int, long>();
            foreach (var cimlet in cimletek)
            {
                if (hatravan < cimlet) continue;
                long mennyi = hatravan / cimlet;
                hatravan -= mennyi * cimlet;
                cimletezett.AddEditItem(cimlet, mennyi);
                if (hatravan == 0) break;
            }
 
            Console.WriteLine("Az összeg címletezve:");
            foreach (var cimlet in cimletezett)
            {
                Console.WriteLine("{0} db {1} Ft-os", cimlet.Value, cimlet.Key);
            }
        }
 
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("--------------------------------------------------");
                Console.WriteLine("Címletező program");
                Console.WriteLine("--------------------------------------------------");
                Console.Write("Add meg a címletezendő összeget: ");
                long input = Convert.ToInt64(Console.ReadLine());
                input = kerekit(input);
                Console.WriteLine("A cimletezeshez a kerekitett osszeg: {0}\n", input);
                Cimletez(input);
                Console.WriteLine("Nyomj egy gombot a kilepeshez...");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Hiba Történt!");
                Console.WriteLine("A hiba leírása: {0}", ex.Message);
            }
        }
    }
}

A program működése

A forráskód csak egy újdonságot tartalmaz. Ez a Kiegeszitok osztály AddEditItem függvénye. Ez egy ún. Extension Method, vagyis kiegészítő metódus. Ráadásul ez a függvény még generikus is. Generikus függvények esetén a függvény neve után <> jelek között vesszővel elválasztva meg kell adni a használt típusokat. A függvény visszatérési értéke természetesen generikus is lehet.

A függvény attól lesz kiegészítő metódus, mert a függvény statikus osztály statikus függvénye  és az első paramétere this kulcsszóval van ellátva. A this kulcsszó egyébként egy objektumon belül az objektumra mutat. Azonban ebben a kontextusban (statikus osztály esetén nincs értelme this kulcsszóval mutatni objektumra) azt jelzi a fordítónak, hogy ez egy kiegészítő metódus, amit a this kulcsszóval megadott objektum típushoz rendeljen hozzá.

Így az AddEditItem metódus minden generikus Dictionary osztályon használható. A függvény egyébként azt csinálja, hogy ha a megadott kulcs alapján a szótár nem tartalmaz egy bizonyos elemet, akkor felveszi azt a megadott értékkel. Ha pedig a szótár tartalmazza a kulcsot, akkor meg a kulcs alapján átírja a kulcshoz tartozó értéket.

A kerekit függvény a magyarországi kerekítési szabályoknak megfelelően 5 vagy 0 végződésre kerekíti a számot. Ehhez a bemeneti számot először elosztja maradékosan 10-el. Így a művelet eredményeképpen kapott maradék 0 és 9 közé fog esni. Ez alapján pedig meghatározza a kimeneti kerekített összeget.

A tényleges címletezést a Cimletez függvény végzi. Itt egy tömbben tárolva vannak az elérhető címletek, méghozzá csökkenő sorrendben. A kimeneti eredményeket egy szótár tárolja. Itt a kulcs a címletet jelöli, míg az érték azt, hogy mennyi alkalommal szerepel az összegben az adott címlet.

A címletezési folyamat egy ciklusból áll, ami végigpróbálja az összes lehetséges címletet a beadott pénzösszegen, amit a folyamatban a hatravan változó tárol. Amennyiben a hátralévő, még nem címletezett összeg nagyobb, mint az aktuálisan próbált címlet, akkor a program elosztja a hátralévő részt az aktuális címlettel, majd a kapott szám és a címlet értékét kivonja a hátralévő részből.

Tehát 32000 Ft esetén első futás esetén az összeg nagyobb, mint 20000. Ezért elvégzi a 32000/20000 műveletet. Mivel ezek egész számként vannak tárolva, az osztás eredménye is egész szám lesz, ami 1. Így a 32000-ből levonásra kerül az 1×20000, marad 12000 címletezetlen összeg.

A folyamat közben az 1 és a 20000 tárolódik a szótárban. A ciklus addig ismétlődik, amíg a hátralévő összeg nem 0.

Ha a címletezéssel végzett a program, akkor a szótárban tárolt értékeket formázottan kiírja a képernyőre.

A fő függvényben a program hibavédelemmel ellátott adatbekérést végez. A bekért szám long típusban tárolódik, így 4 milliárd forintnál nagyobb összegek címletezésére is képes a program (nem mintha egy egyszeri földi halandónak szüksége lenne ilyesmire, de nem lehet tudni, hogy milyen gyorsan inflálódik a pénzünk, illetve a statisztikában az az igazán szép, hogy bármilyen kicsi is a dolgok esélye, fel kell készülni az összes lehetőségre 🙂 .)

A cikkhez tartozó példakódok a https://github.com/webmaster442/csharppeldak címen szerezhetőek be.