Grafikus C# – Windows Forms tanfolyam › 12.3 lecke Windows Forms · 12.3 lecke · osztály-tagok Egy osztály három építőkőből áll: mezőkből (az adatok), tulajdonságokból (a kontrollált hozzáférés az adatokhoz) és metódusokból (a műveletek). A Windows Forms vezérlői is ezekből épülnek: a Mezők, propertyk és metódusok
Text, Width, Enabled tulajdonságok, a Show(), Close() metódusok. Ebben a leckében megtanulod, hogyan tervezz jó osztályt: az adatok legyenek private mezőkben, a külvilág tulajdonságokon és metódusokon át érje el őket, a hibás érték be se kerüljön az objektumba, és a belső gyűjteményt ne lehessen kívülről összekuszálni.
A lecke végére
- megkülönbözteted a mezőt, a tulajdonságot és a metódust, és tudod, mikor melyiket választod;
- automatikus, ellenőrző, számított, csak olvasható tulajdonságot írsz;
- a setterben ellenőrzöd az értéket (
ArgumentException), így az objektum mindig érvényes marad; - a belső listát csak olvashatóként adod ki (
IReadOnlyList<T>); - metódusokat tervezel (paraméterek, visszatérési érték, túlterhelés, opcionális paraméter);
- felismered a tipikus tervezési hibákat (nyilvános mezők, kívülről módosítható lista).
Mielőtt elkezded
Ehhez a leckéhez a 12.1–12.2 lecke és a 4.7 lecke (kivételek) szükséges.
Mező és tulajdonság
public class Tanulo
{
// MEZŐ: az adat tárolója; általában private
private string nev;
// PROPERTY: kontrollált hozzáférés (get = olvasás, set = írás)
public string Nev
{
get { return nev; }
set { nev = value; } // a value a beírt érték
}
// AUTOMATIKUS property: ha nincs extra logika, a fordító létrehozza a rejtett mezőt
public string Osztaly { get; set; } = "10.A";
// kívülről csak olvasható, belül írható
public DateTime Felvetel { get; private set; } = DateTime.Now;
}
// használat: a property úgy néz ki, mint egy mező
Tanulo t = new Tanulo();
t.Nev = "Kiss Anna";
lblNev.Text = t.Nev;
// t.Felvetel = DateTime.Now; // FORDÍTÁSI HIBA: a set private| Mező | Tulajdonság (property) | |
|---|---|---|
| Mi ez? | Az objektum adatának tárolóhelye. | Hozzáférési pont: get és/vagy set kóddal. |
| Láthatóság | Általában private. | Általában public. |
| Ellenőrzés | Nincs: bármi beírható. | A set ellenőrizheti az értéket. |
| Írásvédelem | readonly (csak konstruktorban). | Nincs set, vagy private set. |
| Designer, kötés | Nem használható adatkötéshez. | A Properties ablak és az adatkötés property-ket lát. |
public string nev; mezőbe bárki bármit beírhat (üres szöveget, null értéket), és később nem tudod ellenőrzéssel védeni anélkül, hogy minden hívót átírnál. A property később kiegészíthető logikával, a hívó kódja változatlan marad.{ get; set; } rövidítés a leggyakoribb: a fordító létrehozza a rejtett mezőt. Akkor írj teljes változatot, ha a get vagy set logikát kap.private set azt jelenti: kívülről csak olvasható, az osztályon belül módosítható. Azonosítóknak, létrehozási időnek kiváló.button.Text = "OK" egy tulajdonság set ágát hívja: a vezérlő közben újrarajzolja magát. A property mögött tehát lehet kód: ezért nem mindegy, mit teszel bele.Jósolj!
A Tanulo.Felvetel tulajdonságnak private set van. A TanuloForm kódja t.Felvetel = DateTime.Now; sort tartalmaz. Mikor derül ki a hiba?
Jóslat ellenőrzése
Fordításkor (CS0272: a set elérhetetlen): még a program futtatása előtt. Az osztály védi saját adatát: a fordító kikényszeríti a szabályt.
Ellenőrzés a setterben, számított tulajdonságok
public class Tanulo
{
private string nev = "";
private readonly List<int> jegyek = new List<int>(); // belső gyűjtemény: private
// ELLENŐRZŐ property: az objektum mindig érvényes marad
public string Nev
{
get { return nev; }
set
{
if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("A név nem lehet üres."); }
nev = value.Trim();
}
}
// SZÁMÍTOTT, csak olvasható property: nincs saját tárolója, mindig a jegyekből számolódik
public double Atlag { get { return jegyek.Count == 0 ? 0 : jegyek.Average(); } }
public int JegyekSzama => jegyek.Count; // kifejezés-törzsű rövidítés
// a gyűjtemény csak olvasható nézete: nem lehet kívülről Add/Remove
public IReadOnlyList<int> Jegyek { get { return jegyek; } }
// a módosítás metóduson át, ellenőrzéssel
public void JegyetBeir(int jegy)
{
if (jegy < 1 || jegy > 5) { throw new ArgumentOutOfRangeException("jegy", "A jegy 1 és 5 közötti szám lehet."); }
jegyek.Add(jegy);
}
}
// a hívó oldal: a felület kapja el a kivételt, és közli a felhasználóval
try { tanulo.JegyetBeir((int)nudJegy.Value); }
catch (ArgumentException ex) { tsslUzenet.Text = ex.Message; }Atlag nincs eltárolva: mindig a jegyekből jön. Így sosem lesz elavult (nem kell a jegy hozzáadásakor frissíteni). Olcsó számításhoz property, drága számításhoz metódus (AtlagSzamol()) való.List<int>-et public adod ki, a hívó a metódus megkerülésével bármit beletehet (t.Jegyek.Add(9)). Az IReadOnlyList<int> nézetben csak olvasható a lista. (Az alaplista tehát nem readonly tetszőlegesen: a mező readonly csak azt jelenti, hogy másik listára nem cserélhető.)TryParse, NumericUpDown tartománya), de az osztály soha ne bízzon a hívó előzetes ellenőrzésében.Jósolj!
A Jegyek tulajdonság List<int> típusú és public. A hívó t.Jegyek.Add(9); sort ír. Mi lesz az Atlag értéke, és miért probléma?
Jóslat ellenőrzése
Az átlagba beleszámít a 9-es jegy: az ellenőrzést megkerülték, az objektum érvénytelen állapotba került. Az IReadOnlyList<int> kiadása és a JegyetBeir metódus ezt megakadályozza.
Metódusok tervezése
// művelet, amely változtat az állapoton: igen/nem eredménnyel
public bool TorolUtolsoJegy()
{
if (jegyek.Count == 0) { return false; }
jegyek.RemoveAt(jegyek.Count - 1);
return true;
}
// kérdés az objektumról (nem változtat): visszatérési érték
public int Legjobb() { return jegyek.Count == 0 ? 0 : jegyek.Max(); }
// TÚLTERHELÉS: ugyanaz a név, más paraméterek
public void JegyetBeir(int jegy) { /* ... */ }
public void JegyetBeir(params int[] jegyek) { foreach (int j in jegyek) { JegyetBeir(j); } }
// OPCIONÁLIS paraméter
public string Osszefoglalo(bool reszletes = false)
{
string s = nev + ": átlag " + Atlag.ToString("0.00");
return reszletes ? s + " (jegyek: " + string.Join(", ", jegyek) + ")" : s;
}
// ToString felülírása: a ListBox ezt jeleníti meg
public override string ToString() { return Osszefoglalo(); }t.Osszefoglalo()). Ha a Form kódjában jegyek.Sum() / jegyek.Count szerepel, a logika rossz helyen van.Atlag, Nev). Metódus: művelet, számítás vagy mellékhatás (JegyetBeir, FajlbaMent).= false) egyszerűsíti a gyakori hívást.ToString() felülírása adja meg, hogyan jelenjen meg az objektum szövegként (ListBox, MessageBox). A lst.Items.Add(tanulo) így kiírja az összefoglalót.Jósolj!
A lstTanulok.Items.Add(tanulo) hívás a ToString felülírása nélkül mit jelenít meg a listában?
Jóslat ellenőrzése
A típus teljes nevét (például NaploProjekt.Tanulo): az alap ToString() csak ennyit tud. A felülírás adja a hasznos szöveget.
Mit tegyél ide: a tanulói napló: a tanulók listája, a jegybeírás és a hibaüzenet érvénytelen jegynél.Javasolt fájlnév: winforms-12-3-tanulo.webp · Alt szöveg: A tanulói napló 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. StackOverflowException a property-ben
public string Nev { get { return Nev; } set { Nev = value; } }Hiba és javítás
A property önmagát hívja (végtelen rekurzió). A mögötte álló mezőt kell használni (nev), vagy automatikus property.
2. Az átlag nulla elemnél hibát ad
public double Atlag { get { return jegyek.Average(); } }Hiba és javítás
Üres gyűjteményen az Average() InvalidOperationException. Ellenőrizd a Count == 0 esetet.
3. Nyilvános lista, kerülő út
public List<int> Jegyek = new List<int>();Hiba és javítás
A hívó az ellenőrzés megkerülésével ír a listába. Private mező, IReadOnlyList property, JegyetBeir metódus.
4. A name és a Name összekeveredik
A setter a A mező (kis betű) és a property (nagy betű) különbsége csak a kezdőbetű: figyelj, és használd a Visual Studio Quick Actions (Ctrl+.) automatikus property-generálását.Nev (property) helyett a nev mezőt ellenőrizné, de elírod.Ok és javítás
5. A szóköz-név átmegy
if (value == "") { throw new ArgumentException(); } // a " " és a null átmegyHiba és javítás
string.IsNullOrWhiteSpace(value) kezeli a null, üres és szóközös esetet is.
6. Az Atlag nem frissül
Az Eltárolt, származtatott adat elavul. Számított property: mindig a jegyekből jön, nincs mit frissíteni.Atlag mezőként tárolt, és a jegy beírása után nem számolódik újra.Ok és javítás
Mini projekt: tanulói napló osztályokkal
Készíts naplót, amelyben az adatok és a szabályok a saját osztályban élnek, a Form csak megjeleníti:
- Tanulo osztály:
Nev(ellenőrzött),Osztaly(automatikus),Felvetel(private set), privát jegylista;Jegyek(IReadOnlyList),Atlag,JegyekSzama(számított);JegyetBeir(1–5, kivétel),TorolUtolsoJegy,Legjobb,Osszefoglalo(bool),ToString. - Osztaly (napló) osztály: privát
List<Tanulo>;Hozzaad(Tanulo)(azonos nevű nem vehető fel újra),Torol(Tanulo),Tanulok(IReadOnlyList),OsztalyAtlag,Legjobb()(a legjobb átlagú tanuló, vagynull). - Felület:
txtNev, Felvesz;lstTanulok(aToStringjeleníti meg);nudJegy(1–5), Jegy beírása, Utolsó jegy törlése, Törlés;lblOsztalyAtlag,lblLegjobb; állapotsor. - Hibakezelés: az osztály kivételeit a felület
catch-eli és az állapotsorban közli; semmi kivétel nem omlaszt; nincs kijelölt tanuló: érthető üzenet. - Frissítés: minden módosítás után egyetlen
Frissit()metódus tölti újra a listát és a címkéket; a kijelölés megmarad. - Tesztelés: üres név, szóközös név, ismétlődő név, 0 és 6 jegy, üres tanuló átlaga, törlés jegyek nélkül,
t.Jegyek.Addpróbálkozás (nem fordul), osztályátlag 0 tanulónál.
Megoldás megnyitása (próbáld ki előbb önállóan!)
public class Naplo
{
private readonly List<Tanulo> tanulok = new List<Tanulo>();
public IReadOnlyList<Tanulo> Tanulok { get { return tanulok; } }
public double OsztalyAtlag
{
get { var van = tanulok.Where(t => t.JegyekSzama > 0).ToList(); return van.Count == 0 ? 0 : van.Average(t => t.Atlag); }
}
public void Hozzaad(Tanulo t)
{
if (t == null) { throw new ArgumentNullException("t"); }
if (tanulok.Any(x => string.Equals(x.Nev, t.Nev, StringComparison.CurrentCultureIgnoreCase))) { throw new ArgumentException("Már van ilyen nevű tanuló."); }
tanulok.Add(t);
}
public bool Torol(Tanulo t) { return tanulok.Remove(t); }
public Tanulo Legjobb()
{
return tanulok.Where(t => t.JegyekSzama > 0).OrderByDescending(t => t.Atlag).FirstOrDefault();
}
}
// ---- Form1.cs ----
private readonly Naplo naplo = new Naplo();
private void btnFelvesz_Click(object sender, EventArgs e)
{
try
{
Tanulo t = new Tanulo();
t.Nev = txtNev.Text; // az osztály ellenőriz
naplo.Hozzaad(t);
txtNev.Clear();
Frissit(t);
}
catch (ArgumentException ex) { tsslUzenet.Text = ex.Message; }
}
private void btnJegy_Click(object sender, EventArgs e)
{
Tanulo t = lstTanulok.SelectedItem as Tanulo;
if (t == null) { tsslUzenet.Text = "Előbb jelölj ki egy tanulót."; return; }
try { t.JegyetBeir((int)nudJegy.Value); Frissit(t); }
catch (ArgumentException ex) { tsslUzenet.Text = ex.Message; }
}
private void btnUtolso_Click(object sender, EventArgs e)
{
Tanulo t = lstTanulok.SelectedItem as Tanulo;
if (t == null) { tsslUzenet.Text = "Előbb jelölj ki egy tanulót."; return; }
tsslUzenet.Text = t.TorolUtolsoJegy() ? "Az utolsó jegy törölve." : "Nincs törölhető jegy.";
Frissit(t);
}
private void btnTorol_Click(object sender, EventArgs e)
{
Tanulo t = lstTanulok.SelectedItem as Tanulo;
if (t != null) { naplo.Torol(t); Frissit(null); }
}
private void Frissit(Tanulo kijelolt)
{
lstTanulok.BeginUpdate();
lstTanulok.Items.Clear();
foreach (Tanulo t in naplo.Tanulok) { lstTanulok.Items.Add(t); } // a ToString jeleníti meg
if (kijelolt != null) { lstTanulok.SelectedItem = kijelolt; }
lstTanulok.EndUpdate();
lblOsztalyAtlag.Text = "Osztályátlag: " + naplo.OsztalyAtlag.ToString("0.00");
Tanulo legjobb = naplo.Legjobb();
lblLegjobb.Text = legjobb == null ? "Legjobb: -" : "Legjobb: " + legjobb.Nev;
}Mit tegyél ide: a kész tanulói napló.Javasolt fájlnév: winforms-12-3-kesz.webp · Alt szöveg: A kész tanulói napló
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. Téglalap
Írj A setter kivételt dob 0 vagy negatív értékre; a Teglalap osztályt Szelesseg, Magassag (pozitív), számított Terulet, Kerulet property-vel.Egy lehetséges megoldás
Terulet => Szelesseg * Magassag számított, nincs mezője, sosem lesz elavult.
2. Bankszámla
Az Egyenleg csak olvasható; Befizet, Kivesz metódus. Mi a szabály?Egy lehetséges megoldás
private set az egyenlegen; a Kivesz kivételt dob, ha nincs fedezet; a pozitív összeg ellenőrzése; így az egyenleg csak a metóduson át módosul.
3. ToString
Írd felül egy Pont osztály ToString() metódusát (3; 5) alakra.Egy lehetséges megoldás
public override string ToString() { return "(" + X + "; " + Y + ")"; }. A MessageBox és a ListBox ezt használja.
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. Mező vagy property?
Melyik kerül A mező általában private, melyik public szintre?Megoldás megnyitása
private, a property public.
2. Mit jelent a private set?
Kívülről írható-e? Nem: kívülről csak olvasható, az osztályon belül módosítható.Megoldás megnyitása
Közepes szint
3. Számított property
Írd meg a Teljes nevet (Vezeteknev + " " + Keresztnev) számított property-ként.Megoldás megnyitása
public string Teljes => Vezeteknev + " " + Keresztnev; nincs tároló, sosem avul el.
4. Property vagy metódus?
Melyik: (a) (a) property; (b) metódus (mellékhatás, lassú); (c) property.Atlag; (b) fájlba mentés; (c) Letrehozva?Megoldás megnyitása
Kihívás szint
5. Védőmásolat
Egy Másolatot adsz ki (Lista property IReadOnlyList, de a hívó (List<int>)t.Jegyek típuskényszerítéssel mégis módosítja. Hogyan védekezel?Megoldás megnyitása
jegyek.ToList().AsReadOnly()), vagy ReadOnlyCollection<int> burkolót: a kényszerítés nem a belső listát éri el.
6. init-only
Mire jó az A property csak az objektum létrehozásakor (konstruktor, objektum-inicializáló) állítható; utána változatlan: változatlan (immutable) objektumokhoz.{ get; init; } (C# 9)?Megoldás megnyitása
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Nem ellenőrizhető, és később nem védhető meg a hívók átírása nélkül. A Az érvénytelen értéket kivétellel utasítja el: az objektum érvényes marad. Nem avul el, nincs mit frissíteni. Adatszerű, olcsó: property; művelet, mellékhatás: metódus. Az objektum szöveges megjelenéséhez (1. Miért ne legyen nyilvános a mező?
2. Mi az automatikus property?
{ get; set; }: a fordító a rejtett mezőt létrehozza.3. Mit ellenőriz a setter?
4. Miért számított az Atlag?
5. Hogyan adsz ki gyűjteményt?
IReadOnlyList<T> nézetként, módosítás metóduson át.6. Mikor property, mikor metódus?
7. Mire jó a ToString felülírása?
ListBox, MessageBox).
Mit tudsz már?
- Mezőket, tulajdonságokat és metódusokat tervezel, és tudod, mi hova való.
- Az objektum állapotát a setterben és a metódusokban védeded.
- A belső gyűjteményt csak olvashatóként adod ki.
- A logikát az osztályba teszed, a Form csak megjelenít.
Következő lecke: Konstruktorok és adatátadás
Érvényes objektum már a létrehozáskor; adatátadás a Formok között konstruktorral, tulajdonsággal és eseménnyel.



