Pontosan mi az az IEnumerable vagy IEnumartor?
Az "enumerable" amolyan listázhatóságot jelent.
Általában olyankor használják, mikor egy "kurzort" léptetsz elejétől a végéig (rendszerint next()-el) és ahol áll épp avval végezhetsz műveletet, current() az aktuális elemet szedi ki. Lényegében egy olyan adat sor, amin lépkedhetsz oda-vissza (visszát, ha támogatja az interfész).
Más nyelvekben Iterable interfészként hivatkoznak rá.
Ezek csak metódus interfészek, nyilván te mondod meg, hogy a next() mit csináljon. Azért interfészként használják, mert ha más-más objektumot is ellátsz vele, akkor arról is mondhatjuk, hogy "végig tudunk a tartalmán lépkedni".
Ez az úgynevezett iterátor tervezési minta. Arra a problémára kínál megoldást, hogy van többféle felsorolható adatszerkezeted, például tömb vagy láncolt lista. Sőt, olyan adatszerkezet is szóba jöhet, aminek az elemei a bejárás során dinamikusan generáltak vagy stream-eltek, vagyis amíg be nem járod, az elemei és a sorozat számossága nem ismert. Ezeknek az adatszerkezeteknek a bejárását teszi lehetővé egy egységes felületen.
Ahhoz, hogy egy adatszerkezetet be tudj járni, úgy igazából három dolgot kell tudnod:
1) hányadik elemnél járok (ez csak a belső működéshez szükséges olyan adatszerkezeteknél, aminek az összes eleme ismert, és egy bizonyos elemet indexelve éred el)
2) rá tudok-e állni a következő elemre
3) mi az éppen kiválasztott elem értéke.
Ezt a programozási környezetek úgy szokták megoldani, hogy kínálnak két, többnyire generikus interfészt: IEnumerable<T> és IEnumerator<T>. Ezek közül az IEnumerable<T> nagyon egyszerű:
interface IEnumerable<out T> {
.. IEnumerator<T> GetEnumerator();
}
Ezt az interfészt kell implementálnod, ha azt szeretnéd, hogy az általad írt adatszerkezet felsorolhatóvá váljon. De ez nyilván hozza magával a következő interfészt, az IEnumerator<T>-t, ami az adatszerkezeted bejárását fogja lehetővé tenni:
interface IEnumerator<T> {
.. T Current { get; } //mi az éppen kiválasztott elem
.. bool MoveNext(); //rá tudtam-e lépni a következő elemre
}
Vagyis, ha csinálni akarok egy MyRandomIntEnumerable osztályt, amit szeretnék bejárhatóvá tenni ezzel a tervezési mintával, valami ilyen kódot fogok írni:
class MyRandomIntEnumerable : IEnumerable<int> {
.. private readonly int[] array;
.. public int this[int index] {
.. .. get => array[index];
.. }
.. public int Size {
.. .. get => array.Length;
.. }
.. public MyIntArray(int size) {
.. .. this.array = new int[size];
.. .. this.Generate(size);
.. }
.. private void Generate(int size) {
.. .. Random r = new Random();
.. .. for (int i = 0; i < size; i++) {
.. .. .. array[i] = r.Next(1, 10000);
.. .. }
.. }
.. public IEnumerator<int> GetEnumerator() {
.. .. return new MyRandomIntArrayIterator(this);
.. }
}
class MyRandomIntArrayIterator : IEnumerator<int> {
.. private int index = 0;
.. private readonly MyIntArray source;
.. public int Current {
.. .. get => source[index];
.. }
.. public bool MoveNext() {
.. .. if (index == source.Size)
.. .. .. return false;
.. .. index++;
.. .. return true;
.. }
}
Mint láthatod, az implementációja nem túl bonyolult. Viszont ez a tervezési minta magában a nyelvben is használva van. A foreach ciklus például pont ezen a tervezési mintán alapulva teszi lehetővé az adatszerkezet bejárását, vagyis ha az osztályom helyesen implementálja az IEnumerable<T> interfészt, azonnal használhatom ebben a fajta ciklusban:
MyRandomIntArray myarray = new MyRandomIntArray(10);
foreach (int i in myarray)
.. Console.WriteLine(i);
Másik hatalmas nagy hozománya a válaszom elején említett dinamikusan generált vagy streamelt adatsorok. Ezt a technikát lazy loadingnak is hívják, mivel az elemeket csak akkor töltjük be a memóriába, amikor valóban szükség van rájuk. Vegyük a következő példát, hogy van egy adatforrásom, ami marha sok adatot generál és fel kell dolgoznom:
Mint azt láthatod, ha a teljes adatsort betöltöd a memóriába, akkor ez nagyjából le is zabált 2 GB RAM-ot a feldolgozás során. De vessük be az iterátor mintát erre az esetre:
Ugyanannyi adatot dolgoztam fel, de mivel egyszerre csak egy adatot tartottam a memóriában, 2 GB helyett rögtön elég lett 7 MB a feldolgozáshoz.
Eh, én a linkcsászár :D
Na még egyszer:
Vegyük a következő példát, hogy van egy adatforrásom, ami marha sok adatot generál és fel kell dolgoznom:
Mint azt láthatod, ha a teljes adatsort betöltöd a memóriába, akkor ez nagyjából le is zabált 2 GB RAM-ot a feldolgozás során. De vessük be az iterátor mintát erre az esetre:
További kérdések:
Minden jog fenntartva © 2025, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!