Grafikus C# – Windows Forms tanfolyam › 12.8 lecke Windows Forms · 12.8 lecke · OOP A 12.7 leckében a számítást kiemelted a Formból. Van még egy dolog, amit a Form nem tehet: közvetlenül fájlokat írni és olvasni. Ha a Form a Felület és adatkezelés különválasztása
File.WriteAllText hívást és a JSON formátumot is ismeri, akkor a fájlformátum módosítása (JSON helyett XML, vagy adatbázis) a Form minden pontját érinti, és mentés nélkül nem próbálható ki a program. A megoldás egy tároló osztály (repository), amely a mentést és betöltést végzi, és egy interface, amely megmondja, mit tud a tároló, anélkül hogy megmondaná, hogyan. Ebben a leckében megtanulod az interface használatát, a több megvalósítást (fájl és memória), a függőség konstruktoron át történő átadását, a hibakezelést és az aszinkron mentést.
A lecke végére
- tároló osztályt írsz, amely elrejti a mentés és betöltés részleteit;
- interface-t definiálsz és valósítasz meg, és érted, mire jó a szerződés;
- két tárolót (JSON fájl, memória) cserélsz anélkül, hogy a program többi része változna;
- a függőséget a konstruktoron át adod át (dependency injection, egyszerűen), és a
Program.cs-ben állítod össze; - a tároló hibáit saját kivétellel jelzed, és a Form érthetően jeleníti meg;
- a mentést aszinkron végzed, és a változások nyomon követésével kerülöd a fölösleges mentést.
Mielőtt elkezded
Ehhez a leckéhez a 12.1–12.7 lecke, a 10.6 lecke (JSON), a 10.9 lecke (mentési hibák) és a 11.5 lecke (async) szükséges.
A tároló osztály
private void btnMent_Click(object sender, EventArgs e)
{
string json = JsonSerializer.Serialize(feladatok);
File.WriteAllText("feladatok.json", json); // útvonal, formátum, hibakezelés: mind a Formban
}// a lemezre kerülő forma: egyszerű, nyilvános tulajdonságokkal (a modell és a tárolt forma szétválik)
public class FeladatAdat
{
public string Cim { get; set; } = "";
public DateTime? Hatarido { get; set; }
public Fontossag Fontos { get; set; }
public bool Kesz { get; set; }
}
public class JsonFeladatTarolo
{
private readonly string ut;
public JsonFeladatTarolo(string ut) { this.ut = ut; }
public List<Feladat> Betolt()
{
if (!File.Exists(ut)) { return new List<Feladat>(); } // első indítás: üres lista, nem hiba
string json = File.ReadAllText(ut, Encoding.UTF8);
List<FeladatAdat> adatok = JsonSerializer.Deserialize<List<FeladatAdat>>(json) ?? new List<FeladatAdat>();
List<Feladat> lista = new List<Feladat>();
foreach (FeladatAdat a in adatok)
{
Feladat f = new Feladat(a.Cim) { Hatarido = a.Hatarido, Fontos = a.Fontos };
if (a.Kesz) { f.Elvegez(); }
lista.Add(f);
}
return lista;
}
public void Ment(IEnumerable<Feladat> feladatok)
{
List<FeladatAdat> adatok = feladatok.Select(f => new FeladatAdat { Cim = f.Cim, Hatarido = f.Hatarido, Fontos = f.Fontos, Kesz = f.Kesz }).ToList();
string json = JsonSerializer.Serialize(adatok, new JsonSerializerOptions { WriteIndented = true });
string ideiglenes = ut + ".tmp";
File.WriteAllText(ideiglenes, json, new UTF8Encoding(false)); // előbb ideiglenes fájlba (10.9 lecke)
if (File.Exists(ut)) { File.Delete(ut); }
File.Move(ideiglenes, ut); // majd átnevezés: a félbeszakadt mentés nem rontja el a régi fájlt
}
}Betolt() és Ment(). Ismeri a fájl útvonalát és a formátumot, de nem ismeri a felületet és a számítási szabályokat.FeladatAdat a lemezre kerülő, egyszerű, nyilvános tulajdonságú forma. A Feladat modell védett (private set, ellenőrzés): a tároló a kettő között fordít. Így a fájlformátum változása nem rontja el a modell védelmét.File.Replace atomi cserét ad (Windowsos, azonos meghajtón).Jósolj!
A Feladat osztály Cim tulajdonságának private set setterje van, és a JsonSerializer.Deserialize<List<Feladat>> közvetlenül a modellt töltené be. Mit tesz a System.Text.Json?
Jóslat ellenőrzése
Alapból nem állítja be a privát setterű tulajdonságokat (és paraméteres konstruktorral is csak egyeztetett nevek esetén dolgozik): a Cim üres maradna. Ezért jó a külön FeladatAdat: a tárolt forma szabadon szerializálható, a modell védett.
Interface: szerződés a megvalósítás helyett
// az INTERFACE: megmondja, MIT tud a tároló (nincs benne kód)
public interface IFeladatTarolo
{
List<Feladat> Betolt();
void Ment(IEnumerable<Feladat> feladatok);
}
// 1. megvalósítás: fájlba ment
public class JsonFeladatTarolo : IFeladatTarolo { /* Betolt, Ment: lásd fent */ }
// 2. megvalósítás: csak a memóriában tart (teszthez, vagy ideiglenes használathoz)
public class MemoriaFeladatTarolo : IFeladatTarolo
{
private List<FeladatAdat> adatok = new List<FeladatAdat>();
public int MentesekSzama { get; private set; } // a teszt ellenőrizheti, hányszor mentettek
public List<Feladat> Betolt()
{
return adatok.Select(a => Fordit(a)).ToList();
}
public void Ment(IEnumerable<Feladat> feladatok)
{
adatok = feladatok.Select(f => new FeladatAdat { Cim = f.Cim, Hatarido = f.Hatarido, Fontos = f.Fontos, Kesz = f.Kesz }).ToList();
MentesekSzama++;
}
private static Feladat Fordit(FeladatAdat a)
{
Feladat f = new Feladat(a.Cim) { Hatarido = a.Hatarido, Fontos = a.Fontos };
if (a.Kesz) { f.Elvegez(); }
return f;
}
}: IFeladatTarolo), vállalja, hogy minden tagot megírja. A neve konvenció szerint I-vel kezdődik.MemoriaFeladatTarolo lemez nélkül működik: a tesztek gyorsak, nem hagynak fájlt, és nem függnek a mappa jogosultságaitól. A MentesekSzama megmondja, hányszor mentettek.Jósolj!
Az IFeladatTarolo interface-hez egy új metódust adsz (void Torol()), de a MemoriaFeladatTarolo osztályban nem írod meg. Mi történik?
Jóslat ellenőrzése
Fordítási hiba (CS0535: a MemoriaFeladatTarolo nem valósítja meg az interface Torol() tagját). A fordító kikényszeríti a szerződést: minden megvalósításnak teljesen kell követnie.
A függőség átadása és az összeállítás
// A FELADATLISTA (szolgáltatás): az interface-t kapja, nem a konkrét tárolót
public class FeladatLista
{
private readonly IFeladatTarolo tarolo;
private readonly List<Feladat> feladatok = new List<Feladat>();
public bool Modositott { get; private set; }
public IReadOnlyList<Feladat> Elemek { get { return feladatok; } }
public event EventHandler Valtozott;
public FeladatLista(IFeladatTarolo tarolo) // KONSTRUKTOR-injektálás: kívülről kapja
{
if (tarolo == null) { throw new ArgumentNullException("tarolo"); }
this.tarolo = tarolo;
}
public void Betolt() { feladatok.Clear(); feladatok.AddRange(tarolo.Betolt()); Modositott = false; Jelez(); }
public void Ment() { tarolo.Ment(feladatok); Modositott = false; }
public void Hozzaad(Feladat f) { feladatok.Add(f); Modositott = true; Jelez(); }
public void Torol(Feladat f) { if (feladatok.Remove(f)) { Modositott = true; Jelez(); } }
private void Jelez() { Valtozott?.Invoke(this, EventArgs.Empty); }
}
// A FORM: a szolgáltatást kapja a konstruktorban
public partial class Form1 : Form
{
private readonly FeladatLista lista;
public Form1(FeladatLista lista)
{
InitializeComponent();
this.lista = lista;
lista.Valtozott += (s, e) => Frissit();
}
}
// A PROGRAM.CS: itt áll össze minden ("composition root"): ez az EGYETLEN hely, ahol a konkrét osztályt választod
static void Main()
{
Application.EnableVisualStyles();
string ut = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Feladatlista", "feladatok.json");
Directory.CreateDirectory(Path.GetDirectoryName(ut));
IFeladatTarolo tarolo = new JsonFeladatTarolo(ut); // itt cserélhető: new MemoriaFeladatTarolo()
FeladatLista lista = new FeladatLista(tarolo);
Application.Run(new Form1(lista));
}new JsonFeladatTarolo()), hanem kívülről kapja (a konstruktorban). Így a használó osztály nem függ a konkrét megvalósítástól.Main) áll össze az alkalmazás: te választod a tárolót, a szolgáltatást és a Formot. Ha tárolót cserélsz, csak ezt a sort írod át.Modositott jelzi, hogy van mentetlen változás: a FormClosing rákérdezhet (Mented?), és a Mentés gomb tiltható, ha nincs mit menteni.Valtozott esemény értesíti a Formot: a lista frissítése így egy helyen van, nem minden módosítás után külön. A szolgáltatás nem ismeri a Formot (laza csatolás, 12.4 lecke).Jósolj!
A FeladatLista konstruktora new JsonFeladatTarolo("feladatok.json") hívással maga hozza létre a tárolót. Mit nem tudsz megtenni?
Jóslat ellenőrzése
Nem tudod lemez nélkül tesztelni a FeladatLista-t (mindig a valódi fájlt írja), és nem tudod cserélni a tárolót anélkül, hogy a FeladatLista kódját átírnád. A kívülről kapott függőség mindkettőt megoldja.
Hibák és aszinkron mentés
// saját kivétel: a Form ne ismerje a JsonException, IOException típusokat
public class TaroloHiba : Exception
{
public TaroloHiba(string uzenet, Exception ok) : base(uzenet, ok) { }
}
// a tárolóban: a technikai hibát a felhasználónak szóló üzenetté alakítjuk
public List<Feladat> Betolt()
{
try { /* olvasás és fordítás */ }
catch (JsonException ex) { throw new TaroloHiba("A feladatfájl sérült, nem olvasható.", ex); }
catch (UnauthorizedAccessException ex) { throw new TaroloHiba("Nincs jogosultság a feladatfájlhoz.", ex); }
catch (IOException ex) { throw new TaroloHiba("A feladatfájl nem érhető el: " + ex.Message, ex); }
}
// a Formban: egyetlen catch, érthető üzenet; az eredeti kivétel az InnerException-ben megmarad a naplóhoz
private async void btnMent_Click(object sender, EventArgs e)
{
btnMent.Enabled = false;
try
{
await Task.Run(() => lista.Ment()); // a mentés a háttérben: a felület nem akad (11. fejezet)
tsslUzenet.Text = "Mentve: " + DateTime.Now.ToString("HH:mm:ss");
}
catch (TaroloHiba ex)
{
tsslUzenet.Text = ex.Message;
MessageBox.Show(this, ex.Message, "Mentési hiba", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
finally { btnMent.Enabled = lista.Modositott; }
}TaroloHiba elrejti, hogy fájl, adatbázis vagy hálózat van-e mögötte. A Form egyetlen típust kezel. Az eredeti kivétel (InnerException) a naplóhoz megmarad.await Task.Run(...) a háttérbe viszi (11.5 lecke). Figyelj: mentés közben a lista ne módosuljon (letiltott szerkesztés, vagy a mentés másolatot kap).Ment a lista másolatával dolgozik.Environment.SpecialFolder.ApplicationData (felhasználónként), nem a program mappája (a Program Files alá írni nem szabad).Jósolj!
A tároló a JsonException-t nem alakítja át, és a Form csak TaroloHiba-t kap el. A feladatfájl sérült. Mi történik a program indításakor?
Jóslat ellenőrzése
A JsonException kezeletlenül kilép: a program összeomlik (kezeletlen kivétel ablak). Ezért kell a tárolónak a technikai kivételeket a saját típusára alakítania, és a Formnak minden indításkori betöltést try–catch blokkba tennie.
Mit tegyél ide: a feladatlista, a tároló cseréjével (JSON és memória) és a mentési hiba üzenetével.Javasolt fájlnév: winforms-12-8-tarolo.webp · Alt szöveg: A feladatlista cserélhető tárolóval
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. A betöltött feladatok mind üres címűek
JsonSerializer.Deserialize<List<Feladat>>(json); // a modell privát setterekkelHiba és javítás
A System.Text.Json nem állítja be a privát settereket. Használj külön adat-osztályt (FeladatAdat), és fordítsd át a modellre.
2. CS0535: nincs megvalósítva az interface tagja
Az új tároló osztály nem fordul. Az osztály nem írta meg az interface minden tagját. Visual Studio: Ctrl+. → Implement interface.Ok és javítás
3. A program összeomlik sérült fájlnál
A A tároló alakítsa JsonException kezeletlenül kilép.Ok és javítás
TaroloHiba típusra, a Form pedig kapja el a betöltést. Sérült fájlnál ajánlj felhasználói döntést (üres lista, vagy a mentett másolat visszaállítása: 10.9 lecke).
4. A teszt a lemezt módosítja
public FeladatLista() { tarolo = new JsonFeladatTarolo("feladatok.json"); }Hiba és javítás
A függőség beégetett. Add át a konstruktorban (IFeladatTarolo), és a tesztben MemoriaFeladatTarolo-t használj.
5. A mentés közben kivétel a lista módosításakor
Az A háttérszál a listát járja be, a felület módosítja. Tiltsd a szerkesztést mentés közben, vagy mentés előtt másolatot készíts (InvalidOperationException jelenik meg: a gyűjtemény módosult.Ok és javítás
feladatok.ToList()).
6. A mentett fájl mindig elveszik a program mappájából
Telepítés után a mentés A program a saját mappájába ír (Program Files: nincs írási jog). Használd az UnauthorizedAccessException-t dob.Ok és javítás
ApplicationData mappát.
Mini projekt: feladatlista cserélhető tárolóval
Készíts feladatlistát, amelynek a tárolása interface mögött van, és két tárolóval is működik:
- Modell: a
Feladat(12.6 lecke:Cim,Hatarido,Fontos,Kesz,Elvegez(),Lejart(most)). - FeladatAdat (DTO), és az interface:
IFeladatTarolo(Betolt,Ment). - JsonFeladatTarolo: JSON fájl, ideiglenes fájllal és átnevezéssel; az első indításnál üres lista; a hibák
TaroloHibavá alakítva; sérült fájl esetén a régi mentés (.bak) megőrzése. - MemoriaFeladatTarolo: a
MentesekSzamaszámlálóval. - FeladatLista szolgáltatás: konstruktorban kapja a tárolót;
Hozzaad,Torol,Elvegez,Betolt,Ment,Modositott,Valtozottesemény; a lejártak száma (LejartDb(most)). - Form:
lstFeladatok(CheckedListBoxvagyListBox),txtCim,dtpHatarido, Hozzáad, Kész, Törlés, Mentés (aszinkron);lblLejart; aFormClosingmentetlen változásnál rákérdez (Mented?). - Program.cs: a
cmbTarolohelyett a parancssori kapcsoló (--memoria) vagy egy konstans dönti el, melyik tárolót adja át; a Formban nincsnew JsonFeladatTarolo. - Tesztek (
MemoriaFeladatTarolosegítségével, lemez nélkül): hozzáadás utánModositottigaz, mentés után hamis; aMentés újraBetoltugyanazokat a feladatokat adja (cím, kész, fontosság);MentesekSzama; üres lista; aTorolnem létező elemre nem módosít. - Teszteld: a program újraindítása után a feladatok megvannak; sérült JSON (kézzel elrontva); olvasásvédett fájl; a tároló cseréje (nincs változás a Formban); a bezárás mentetlen változással.
Megoldás megnyitása (próbáld ki előbb önállóan!)
// A JsonFeladatTarolo, MemoriaFeladatTarolo, FeladatAdat, IFeladatTarolo, TaroloHiba a leckében van.
// A JsonFeladatTarolo.Ment kiegészítése a biztonsági másolattal:
public void Ment(IEnumerable<Feladat> feladatok)
{
try
{
List<FeladatAdat> adatok = feladatok.Select(f => new FeladatAdat { Cim = f.Cim, Hatarido = f.Hatarido, Fontos = f.Fontos, Kesz = f.Kesz }).ToList();
string json = JsonSerializer.Serialize(adatok, new JsonSerializerOptions { WriteIndented = true });
string tmp = ut + ".tmp";
File.WriteAllText(tmp, json, new UTF8Encoding(false));
if (File.Exists(ut)) { File.Copy(ut, ut + ".bak", true); File.Delete(ut); }
File.Move(tmp, ut);
}
catch (UnauthorizedAccessException ex) { throw new TaroloHiba("Nincs jogosultság a mentéshez.", ex); }
catch (IOException ex) { throw new TaroloHiba("A mentés nem sikerült: " + ex.Message, ex); }
}
// FeladatLista: lásd a leckében, kiegészítve:
public void Elvegez(Feladat f) { f.Elvegez(); Modositott = true; Jelez(); }
public int LejartDb(DateTime most) { return feladatok.Count(f => f.Lejart(most)); }
// ---- Tesztek (lemez nélkül) ----
public static class Tesztek
{
private static int hibak;
private static void Igaz(bool f, string nev)
{
if (!f) { hibak++; System.Diagnostics.Debug.WriteLine("HIBA " + nev); }
else { System.Diagnostics.Debug.WriteLine("ok " + nev); }
}
public static int Mindet()
{
hibak = 0;
MemoriaFeladatTarolo tarolo = new MemoriaFeladatTarolo();
FeladatLista lista = new FeladatLista(tarolo);
Igaz(!lista.Modositott, "új lista nem módosított");
lista.Hozzaad(new Feladat("Házi feladat") { Fontos = Fontossag.Magas });
lista.Hozzaad(new Feladat("Bevásárlás"));
Igaz(lista.Modositott, "hozzáadás után módosított");
lista.Elvegez(lista.Elemek[1]);
lista.Ment();
Igaz(!lista.Modositott, "mentés után nem módosított");
Igaz(tarolo.MentesekSzama == 1, "egyszer mentettek");
FeladatLista masik = new FeladatLista(tarolo);
masik.Betolt();
Igaz(masik.Elemek.Count == 2, "két feladat töltődött vissza");
Igaz(masik.Elemek[0].Cim == "Házi feladat" && masik.Elemek[0].Fontos == Fontossag.Magas, "az első feladat adatai");
Igaz(masik.Elemek[1].Kesz, "a kész jelző megmaradt");
masik.Torol(new Feladat("nem létezik"));
Igaz(!masik.Modositott, "nem létező törlés nem módosít");
return hibak;
}
}
// ---- Form1.cs (részlet) ----
private readonly FeladatLista lista;
public Form1(FeladatLista lista)
{
InitializeComponent();
this.lista = lista;
lista.Valtozott += (s, e) => Frissit();
try { lista.Betolt(); }
catch (TaroloHiba ex) { tsslUzenet.Text = ex.Message; }
}
private void Frissit()
{
lstFeladatok.Items.Clear();
foreach (Feladat f in lista.Elemek)
{
lstFeladatok.Items.Add((f.Kesz ? "✔ " : f.Lejart(DateTime.Now) ? "⚠ " : " ") + f.Cim, f.Kesz);
}
lblLejart.Text = "Lejárt: " + lista.LejartDb(DateTime.Now);
btnMent.Enabled = lista.Modositott;
}
private void btnHozzaad_Click(object sender, EventArgs e)
{
try { lista.Hozzaad(new Feladat(txtCim.Text) { Hatarido = dtpHatarido.Checked ? (DateTime?)dtpHatarido.Value : null }); txtCim.Clear(); }
catch (ArgumentException ex) { tsslUzenet.Text = ex.Message; }
}
private void btnKesz_Click(object sender, EventArgs e)
{
int i = lstFeladatok.SelectedIndex;
if (i >= 0) { lista.Elvegez(lista.Elemek[i]); }
}
private void btnTorles_Click(object sender, EventArgs e)
{
int i = lstFeladatok.SelectedIndex;
if (i >= 0) { lista.Torol(lista.Elemek[i]); }
}
private async void btnMent_Click(object sender, EventArgs e)
{
btnMent.Enabled = false;
try
{
await Task.Run(() => lista.Ment());
tsslUzenet.Text = "Mentve: " + DateTime.Now.ToString("HH:mm:ss");
}
catch (TaroloHiba ex) { tsslUzenet.Text = ex.Message; MessageBox.Show(this, ex.Message, "Mentési hiba", MessageBoxButtons.OK, MessageBoxIcon.Warning); }
finally { btnMent.Enabled = lista.Modositott; }
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!lista.Modositott) { return; }
DialogResult v = MessageBox.Show(this, "Mentetlen változások vannak. Mented?", Text, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (v == DialogResult.Cancel) { e.Cancel = true; return; }
if (v == DialogResult.Yes)
{
try { lista.Ment(); }
catch (TaroloHiba ex) { MessageBox.Show(this, ex.Message, "Mentési hiba"); e.Cancel = true; }
}
}Mit tegyél ide: a kész feladatlista.Javasolt fájlnév: winforms-12-8-kesz.webp · Alt szöveg: A kész feladatlista
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. CSV tároló
Írj Semmi: csak a CsvFeladatTarolo osztályt ugyanahhoz az interface-hez (10.5 lecke). Mi változik a Formban?Egy lehetséges megoldás
Program.cs egy sora (new CsvFeladatTarolo(ut)). Ez az interface lényege.
2. Naplózó dekorátor
Írj tárolót, amely egy másik tárolót csomagol, és naplózza a mentéseket.Egy lehetséges megoldás
NaplozoTarolo : IFeladatTarolo, konstruktorban kapja a belső tárolót; a Ment naplóz, majd továbbhív. Az új viselkedés nem módosít meglévő osztályt.
3. Automatikus mentés
5 percenként mentsen a program, ha van módosítás. Hová teszed? A Form egy Egy lehetséges megoldás
Timer-e (11.1 lecke) a Modositott jelző alapján hívja az aszinkron mentést; a FeladatLista nem tud az időzítésről.
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. Interface
Mi az interface? Szerződés: megmondja, mit tud egy osztály, a megvalósítás nélkül.Megoldás megnyitása
2. Mit tud a tároló?
Mi a tároló felelőssége, és mi nem? Mentés és betöltés, a formátum és a hibák kezelése. Nem ismeri a felületet és az üzleti szabályokat.Megoldás megnyitása
Közepes szint
3. Hová tartozik?
Hová kerül: (a) (a) new JsonFeladatTarolo(...); (b) a „nincs mentve” jelző?Megoldás megnyitása
Program.cs (composition root); (b) a FeladatLista (Modositott).
4. DTO
Miért külön A tárolt forma szabadon szerializálható, a modell védett marad (privát setter, ellenőrzés); a fájlformátum változása nem érinti a modellt.FeladatAdat?Megoldás megnyitása
Kihívás szint
5. Interface vagy absztrakt osztály?
Mikor melyiket? Az interface csak szerződés (több is megvalósítható egy osztályban); az absztrakt osztály közös kódot is tarthat, de csak egy öröklődhet. Kezdd interface-szel.Megoldás megnyitása
6. Betöltés a konstruktorban?
Miért rossz, ha a A konstruktor kivételt dobhat (sérült fájl), nem aszinkron, és a tesztelés is nehezebb. Külön FeladatLista konstruktora tölt be?Megoldás megnyitása
Betolt() metódus jobb, amelyet a hívó kezelhet.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Mert a formátum és az útvonal módosítása a Form minden pontját érintené, és nem lenne tesztelhető. Az osztály a függőségét kívülről kapja (például a konstruktorban), nem maga hozza létre. Az a hely ( Lemez nélküli, gyors tesztekhez. A Form egyetlen típust kezel, és nem ismeri a technikai kivételeket. Az Mentetlen változás jelzésére: rákérdezés bezáráskor, a Mentés gomb állapota.1. Miért nem ír a Form közvetlenül fájlt?
2. Mi a dependency injection?
3. Mi a composition root?
Main), ahol az alkalmazás részei összeállnak.4. Miért van MemoriaFeladatTarolo?
5. Mi a TaroloHiba haszna?
6. Hová ment a program felhasználói adatot?
ApplicationData mappába, nem a program mappájába.7. Mire jó a Modositott jelző?
Mit tudsz már?
- Tároló osztályt írsz, amely elrejti a mentés részleteit.
- Interface-t definiálsz és több megvalósítással használod.
- A függőséget a konstruktoron át adod át, és a
Program.cs-ben állítod össze. - A tároló hibáit saját kivétellel jelzed, és a mentést aszinkron végzed.
Következő lecke: Egyszerű alkalmazásarchitektúra
A rétegek (modell, szolgáltatás, tároló, felület) összefoglalása, a mappaszerkezet, a függőségek iránya és egy egyszerű felület-szétválasztó minta.



