Grafikus C# – Windows Forms tanfolyam › 12.5 lecke Windows Forms · 12.5 lecke · OOP Egy osztály nem azért van, hogy mindenki beleírjon az adataiba. A hozzáférési szintek (Hozzáférési szintek
public, private, protected, internal) azt szabályozzák, ki láthatja és módosíthatja az osztály tagjait. Az egységbezárás (encapsulation) elve szerint az osztály belső állapota rejtett, kívülről csak néhány, gondosan megtervezett nyilvános művelet érhető el. Így az objektum mindig érvényes állapotban marad, és az osztály belseje szabadon módosítható, anélkül, hogy a többi kód eltörne. Ebben a leckében megtanulod a szinteket, az „annyit mutass, amennyi szükséges” szabályt, és azt, hogyan alkalmazd ezt a Formokra.
A lecke végére
- ismered a hozzáférési szinteket (
public,private,protected,internal, alapértelmezések); - megvéded az objektum belső állapotát, és megőrzöd az invariánst (a mindig igaz feltételt);
- a minimális nyilvános felület elvét alkalmazod;
- tudod, miért
privatea Designer vezérlőinek mezője, és hogyan teszel közzé adatot tulajdonsággal; - használod a
readonly,constésprivate setmódosítókat; - értelmezed a
CS0122hibát (elérhetetlen védelmi szint miatt).
Mielőtt elkezded
Ehhez a leckéhez a 12.1–12.4 lecke szükséges (osztály, tulajdonság, konstruktor).
A hozzáférési szintek
| Módosító | Ki éri el? | Mikor? |
|---|---|---|
public | Bárki, bárhonnan. | Az osztály szándékos, nyilvános felülete. |
private | Csak az osztály saját kódja. | Mezők, segédmetódusok: az alapértelmezés a tagoknál. |
protected | Az osztály és a leszármazottai. | Öröklésnél a leszármazottnak szánt tagok. |
internal | Ugyanabban a projektben (assembly). | Projekten belüli segédosztályok; az alapértelmezés az osztályoknál. |
protected internal | Ugyanaz a projekt vagy a leszármazott. | Ritka. |
private protected | Ugyanaz a projekt és a leszármazott. | Ritka (C# 7.2). |
public class Peldak
{
public int Nyilvanos; // bárhonnan
private int belso; // csak a Peldak osztályból
int alapertelmezett; // kiírás nélkül: private
protected int leszarmazottnak; // a Peldak és a leszármazottai
internal int projektenBelul; // ugyanabban a projektben
}
class Segedosztaly { } // kiírás nélkül: internal (a projekten kívülről nem látszik)
Peldak p = new Peldak();
p.Nyilvanos = 1; // rendben
p.belso = 2; // CS0122: "a Peldak.belso elérhetetlen a védelmi szintje miatt"private, az osztályok alapból internal. A szintet mégis érdemes kiírni: az olvasó azonnal látja a szándékot.private), és csak akkor bővíts, ha tényleg kell. A nyilvános tagot később nehéz visszavonni (más kód már használja), a privátot bármikor megnyithatod.internal a lefordított egységre (egy projekt, egy .exe vagy .dll) vonatkozik. Egy WinForms alkalmazásban, egyetlen projektnél, az internal gyakorlatilag „az egész program”.CS0122): a program el sem indul. Ez a védelem: a hibát a fordító találja meg, nem a felhasználó.Jósolj!
Egy osztálynak int szamlalo; mezője van, módosító nélkül. Egy másik osztály kódja x.szamlalo = 5;. Mi történik?
Jóslat ellenőrzése
Fordítási hiba (CS0122): módosító nélkül a tag private, a másik osztály nem éri el.
Egységbezárás és az invariáns
// ROSSZ: a mező nyilvános, bárki elronthatja
public class RosszSzamla { public decimal Egyenleg; }
RosszSzamla r = new RosszSzamla();
r.Egyenleg = -1000000; // semmi nem védi: az invariáns ("nem negatív") megsérült
// JÓ: a belső állapot privát, a művelet őrzi az invariánst
public class Bankszamla
{
private decimal egyenleg; // rejtett
private readonly List<string> naplo = new List<string>();
public decimal Egyenleg { get { return egyenleg; } } // kívülről csak olvasható
public IReadOnlyList<string> Naplo { get { return naplo; } } // a lista módosítható másolata NEM megy ki
public void Befizet(decimal osszeg)
{
if (osszeg <= 0) { throw new ArgumentOutOfRangeException("osszeg", "Pozitív összeg kell."); }
egyenleg += osszeg;
Naploz("Befizetés: +" + osszeg);
}
public bool Kivesz(decimal osszeg)
{
if (osszeg <= 0 || osszeg > egyenleg) { return false; } // az egyenleg sosem lesz negatív
egyenleg -= osszeg;
Naploz("Kivétel: -" + osszeg);
return true;
}
private void Naploz(string s) { naplo.Add(DateTime.Now.ToString("HH:mm:ss") + " " + s); } // belső segéd: nem nyilvános
}Befizet, Kivesz), nem nyers adatmezők. A hívó nem tudja és nem is kell tudnia, hogyan tárolódik az egyenleg.egyenleg privát, később decimal helyett fillérekben (long) tárolhatod: a külvilág kódja nem törik el. Ez az egységbezárás szabadsága.naplo listát közvetlenül adnád ki, a hívó törölhetné az elemeit. Az IReadOnlyList<T> csak olvasható nézetet ad (a belső lista védve marad).Jósolj!
A Bankszamla osztályban a public List<string> Naplo { get; } tulajdonság a belső listát adja ki. A hívó szamla.Naplo.Clear(); kódot ír. Mi történik?
Jóslat ellenőrzése
A napló kiürül: a get-only tulajdonság csak a hivatkozást védi (nem lehet másik listát hozzárendelni), de a lista tartalmát nem. Ezért kell az IReadOnlyList<T>, vagy másolat (new List<string>(naplo)).
Hozzáférési szintek a Formokban
// a Designer a vezérlők mezőit PRIVÁTRA generálja: kívülről nem érhetők el
private System.Windows.Forms.TextBox txtNev;
// ha kívülről kell az érték: tulajdonságot adunk, nem a vezérlőt tesszük nyilvánossá
public string Nev
{
get { return txtNev.Text.Trim(); }
set { txtNev.Text = value; }
}
// ROSSZ megoldás: a vezérlő nyilvánossá tétele (Properties → Modifiers = Public)
// a hívó bármit megtehet vele: Visible=false, Text=..., Dispose()... és a Form belseje nem cserélhető
// az eseménykezelők privátok: nincs rá szükség, hogy más hívja
private void btnMent_Click(object sender, EventArgs e) { /* ... */ }
// a nyilvános felület: csak ami a hívónak kell
public event EventHandler Mentve;
public Szemely Eredmeny { get; private set; }private mezőként generálja őket. Ha a Properties → Modifiers beállítást Public-ra állítod, a hívó közvetlenül hozzáférne: szoros csatolás (12.4 lecke).public tulajdonságot adj (Nev), amely a vezérlőt elrejti. Ha később a TextBox-ot ComboBox-ra cseréled, csak a tulajdonság belseje változik.btnMent_Click senkinek sem hívható: nincs oka nyilvánosnak lenni. A Designer is privátot generál.Jósolj!
A főablak kódja szerkeszto.txtNev.Text = "x"; sort tartalmaz, és a txtNev a Designer szerint private. Mi történik?
Jóslat ellenőrzése
Fordítási hiba (CS0122). A helyes megoldás nem a Modifiers = Public, hanem egy Nev tulajdonság a SzerkesztoForm-ban.
readonly, const, private set
public class Beallitasok
{
public const int MaxElem = 100; // fordítási idejű állandó, soha nem változik
private readonly string fajlUt; // csak a konstruktorban kaphat értéket
public Beallitasok(string ut) { fajlUt = ut; } // a readonly mező itt még írható
public string FajlUt { get { return fajlUt; } }
public int Elemek { get; private set; } // kívülről olvasható, csak belülről írható
public void Hozzaad()
{
if (Elemek < MaxElem) { Elemek++; }
}
}const double Pi2 = 6.28). Implicit static; csak egyszerű típus lehet.const; objektumonként más, de fix: readonly mező; kívülről olvasható, belülről változó: private set.Jósolj!
A fajlUt readonly mezőt egy metódus (nem a konstruktor) próbálja módosítani: fajlUt = "uj.txt";. Mi történik?
Jóslat ellenőrzése
Fordítási hiba (CS0191): a readonly mezőt csak a deklarációnál vagy a konstruktorban lehet írni.
Mit tegyél ide: a bankszámla-labor: sikeres és elutasított művelet, a napló.Javasolt fájlnév: winforms-12-5-bankszamla.webp · Alt szöveg: A bankszámla-labor program
Hibakeresés: te vagy a hibadetektív
Mi a baj, és hogyan javítod?
Gondold végig az okot, és csak utána nyisd le a megoldást.
1. CS0122: elérhetetlen a védelmi szint miatt
szamla.egyenleg = 100; // private mezőHiba és javítás
Nem a mezőt kell nyilvánossá tenni: használd a Befizet műveletet. A mező az osztály belső ügye.
2. A negatív egyenleg
public decimal Egyenleg { get; set; }Hiba és javítás
A nyilvános set megkerüli az ellenőrzést. private set, és a módosítás a Befizet / Kivesz metóduson át.
3. A napló kívülről törölhető
public List<string> Naplo { get; } = new List<string>();Hiba és javítás
A get-only a hivatkozást védi, nem a tartalmat. IReadOnlyList<string> a kimenő típus.
4. A vezérlő nyilvános lett
A hívó közvetlenül írja a A Modifiers = Public beállítás szoros csatolást okoz. Állítsd vissza txtNev.Text értéket, és a Form átalakítása minden hívást eltör.Ok és javítás
Private-ra, és adj tulajdonságot.
5. CS0191: a readonly mező nem írható
private readonly int max = 10;
void Allit() { max = 20; }Hiba és javítás
A readonly mező csak a konstruktorban (vagy a deklarációnál) írható. Ha változnia kell, ne legyen readonly.
6. Minden public
Az osztály minden tagja Később nehéz átalakítani, mert bárki használja. Fordítsd meg: minden legyen public, mert „úgy működik”.Ok és javítás
private, és csak a szükségeset nyisd meg.
Mini projekt: bankszámla-labor
Készíts programot, amely egy védett belső állapotú számlát kezel, és az egységbezárás előnyeit mutatja:
- Bankszamla osztály: privát
egyenleg,private readonlytulajdonos és azonosító;Egyenleg(csak olvasható),Tulajdonos; a konstruktor kezdő összeggel (nem negatív);Befizet(kivétel hibás összegre),Kivesz(bool),Atutal(Bankszamla, osszeg). - Napló: privát lista,
IReadOnlyList<string> Naplo; a belsőNaplozprivát metódus. - Limit:
public const decimal NapiLimit = 200000; aKivesza napi összeg ellenőrzését is végzi (privát mező a mai kivétekről). - Felület: két számla (Anna, Béla);
nudOsszeg, Befizet, Kivesz, Átutal Annáról Bélára; az egyenlegek és a napló kijelzése; a hibák az állapotsorban. - Védelem-kísérletek: a leírásban jegyezd fel, mit írna a fordító, ha a
szamla.egyenleg = 1000;sort kikommentezett kódként beírnád (CS0122), és ha aNaploList<string>lenne (aClear()hatása). - ToString(): Anna: 1 250 Ft (a ListBoxban használható).
- Hibatűrés: negatív és nulla összeg, túl nagy kivétel, átutalás önmagára,
nullcélszámla, a napi limit túllépése. - Teszteld: minden hibás eset, az egyenleg összege átutalás után (az összes pénz megmarad), a napló sorrendje.
Megoldás megnyitása (próbáld ki előbb önállóan!)
public class Bankszamla
{
public const decimal NapiLimit = 200000m;
private decimal egyenleg;
private decimal maiKivet = 0;
private DateTime maiNap = DateTime.Today;
private readonly List<string> naplo = new List<string>();
private readonly string tulajdonos;
public decimal Egyenleg { get { return egyenleg; } }
public string Tulajdonos { get { return tulajdonos; } }
public IReadOnlyList<string> Naplo { get { return naplo; } }
public Bankszamla(string tulajdonos, decimal kezdo)
{
if (string.IsNullOrWhiteSpace(tulajdonos)) { throw new ArgumentException("A tulajdonos kötelező.", "tulajdonos"); }
if (kezdo < 0) { throw new ArgumentOutOfRangeException("kezdo", "A kezdő összeg nem lehet negatív."); }
this.tulajdonos = tulajdonos.Trim();
egyenleg = kezdo;
Naploz("Megnyitás: " + kezdo.ToString("N0"));
}
public void Befizet(decimal osszeg)
{
if (osszeg <= 0) { throw new ArgumentOutOfRangeException("osszeg", "Pozitív összeg kell."); }
egyenleg += osszeg;
Naploz("Befizetés: +" + osszeg.ToString("N0"));
}
public bool Kivesz(decimal osszeg)
{
NapFrissit();
if (osszeg <= 0 || osszeg > egyenleg || maiKivet + osszeg > NapiLimit)
{
Naploz("Elutasítva: -" + osszeg.ToString("N0"));
return false;
}
egyenleg -= osszeg;
maiKivet += osszeg;
Naploz("Kivétel: -" + osszeg.ToString("N0"));
return true;
}
public bool Atutal(Bankszamla cel, decimal osszeg)
{
if (cel == null) { throw new ArgumentNullException("cel"); }
if (ReferenceEquals(cel, this)) { throw new ArgumentException("Önmagára nem utalhatsz.", "cel"); }
if (!Kivesz(osszeg)) { return false; }
cel.Befizet(osszeg);
Naploz("Átutalás → " + cel.Tulajdonos);
return true;
}
private void NapFrissit()
{
if (maiNap != DateTime.Today) { maiNap = DateTime.Today; maiKivet = 0; }
}
private void Naploz(string s) { naplo.Add(DateTime.Now.ToString("HH:mm:ss") + " " + s); }
public override string ToString() { return tulajdonos + ": " + egyenleg.ToString("N0") + " Ft"; }
}
// ---- Form1.cs ----
private readonly Bankszamla anna = new Bankszamla("Anna", 50000);
private readonly Bankszamla bela = new Bankszamla("Béla", 20000);
private void Frissit()
{
lblAnna.Text = anna.ToString();
lblBela.Text = bela.ToString();
lstNaplo.Items.Clear();
foreach (string s in anna.Naplo) { lstNaplo.Items.Add("Anna " + s); }
foreach (string s in bela.Naplo) { lstNaplo.Items.Add("Béla " + s); }
}
private void Vegrehajt(Func<bool> muvelet, string hiba)
{
try { tsslUzenet.Text = muvelet() ? "Kész." : hiba; }
catch (ArgumentException ex) { tsslUzenet.Text = ex.Message; } // ArgumentOutOfRangeException is ArgumentException
Frissit();
}
private void btnBefizet_Click(object sender, EventArgs e) { Vegrehajt(() => { anna.Befizet(nudOsszeg.Value); return true; }, ""); }
private void btnKivesz_Click(object sender, EventArgs e) { Vegrehajt(() => anna.Kivesz(nudOsszeg.Value), "Elutasítva (egyenleg vagy napi limit)."); }
private void btnAtutal_Click(object sender, EventArgs e) { Vegrehajt(() => anna.Atutal(bela, nudOsszeg.Value), "Az átutalás elutasítva."); }Mit tegyél ide: a kész bankszámla-labor.Javasolt fájlnév: winforms-12-5-kesz.webp · Alt szöveg: A kész bankszámla-labor
Három önálló gyakorlófeladat
Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.
1. Hőmérő
Írj Privát mező, nyilvános tulajdonság Homero osztályt, amelynek a hőmérséklete nem mehet −273,15 °C alá. Hogyan védi az invariánst?Egy lehetséges megoldás
set ellenőrzéssel (kivétel a határ alatt), vagy private set és Allit(double) metódus.
2. Védett lista
Egy osztály listát tart, amit kívülről nem lehet módosítani. Három lehetőség?Egy lehetséges megoldás
IReadOnlyList<T> kimenet; másolat visszaadása (new List<T>(lista)); ReadOnlyCollection<T> (lista.AsReadOnly()).
3. Formok áttekintése
Nézd át egy korábbi projektedet: melyik vezérlő lett nyilvános, és minek? Keresd a Egy lehetséges megoldás
Modifiers = Public beállítást a Designerben (public System.Windows.Forms... a .Designer.cs végén). Cseréld privátra, és adj tulajdonságot a szükséges értékhez.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Alapértelmezések
Mi az alapértelmezett szint egy mezőnél és egy osztálynál? A mező: Megoldás megnyitása
private; az osztály: internal.
2. Melyik kell?
Melyik módosítót használod egy kívülről olvasható, belülről írható számlálóra?Megoldás megnyitása
public int Szamlalo { get; private set; }
Közepes szint
3. const vagy readonly?
Egy objektum létrehozási időpontját tárolod: melyik?Megoldás megnyitása
readonly: objektumonként más (futáskor dől el), de nem változik. A const fordításkor ismert értékhez való.
4. Invariáns
Mi az invariáns, és hogyan védi az egységbezárás? Az objektum egész életében igaz feltétel; csak az osztály saját, ellenőrző műveletei módosíthatják az állapotot.Megoldás megnyitása
Kihívás szint
5. Tulajdonság és list
Miért nem véd a A public List<T> L { get; }?Megoldás megnyitása
get-only a hivatkozás cseréjét tiltja, a lista tartalmát bárki módosíthatja (Add, Clear).
6. internal
Mikor számít a különbség a Ha több projekt (osztálykönyvtár) van: az public és az internal között?Megoldás megnyitása
internal tag csak a saját projektben látszik. Egyetlen projektnél gyakorlatilag nincs különbség.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A belső állapot elrejtése; kívülről csak a gondosan megtervezett műveletek érhetők el. Csak az osztály saját kódja éri el. Nyilvános tulajdonsággal, nem a vezérlő nyilvánossá tételével. Elérhetetlen tag a védelmi szint miatt (fordítási hiba). Kívülről olvasható, csak belülről írható tulajdonság. A legszűkebb szinttel (1. Mi az egységbezárás?
2. Mi a private?
3. Mit generál a Designer a vezérlőkre?
private mezőket.4. Hogyan teszel közzé adatot a Formból?
5. Mit jelent a CS0122?
6. Mire való a private set?
7. Melyik elv szerint kezdesz?
private), és csak szükség esetén bővítesz.
Mit tudsz már?
- Ismered a hozzáférési szinteket és az alapértelmezéseket.
- Megvéded az objektum állapotát és az invariánst.
- A Formokban privát vezérlőket és nyilvános tulajdonságokat használsz.
- Helyesen használod a
readonly,constésprivate setmódosítókat.
Következő lecke: Adatmodellek és felelősségek
Az adatot hordozó osztályok (modellek) tervezése, az egy felelősség elve, a modell és a felület szétválasztása.



