250 likes | 393 Views
Enumerable collection. int [] pins = { 9, 3, 7, 2 }; foreach ( int pin in pins) { Console.WriteLine (pin); } For each kolekcija se mo že primeniti samo na enumerable kolekcije – kolekcije koje implementiraju System.Collections.IEnumerable interface
E N D
Enumerable collection • int[] pins = { 9, 3, 7, 2 }; • foreach (int pin in pins) • { • Console.WriteLine(pin); • } • For each kolekcija se može primeniti samo na enumerable kolekcije – kolekcije koje implementiraju System.Collections.IEnumerable interface • System.Array classimplementiraIEnumerableinterface.
IEnumerable interface • IEnumerableinterface deklariše samo jedan metod koji se naziva GetEnumerator • IEnumeratorGetEnumerator(); • GetEnumeratormethod vraća objekat koji implementira System.Collections.IEnumerator interface • Taj objekat se koristi za prolazak kroz elemente kolekcije • IEnumeratorinterface deklariše sledeća svojstva i metode: • object Current { get; } • boolMoveNext(); • void Reset();
IEnumerator interface • Enumerator – objekat koji implementira IEnumerator interface se može zamisliti kao pokazivač koji pokazuje na elemente u listi. • Na početku, pokazuje ispred prvog elementa • Metod MoveNextpomera pokazivač za jedno mesto i vraća podatak tipa bool ako na sledećoj poziciji postoji element kolekcije, a false ako ga nema • Svojstvo Current se koristi za pristup elementima kolekcije – vraća objekat kolekcije • Metod Reset vraća pokazivač na početak liste – ispred prvog elementa
For each - objašnjenje • For each struktura kreira enumerator pozivanjem metoda kolekcije GetEnumerator • Kroz elemente kolekcije se prolazi uzastopnim pozivanjem metoda MoveNext dok se ne dođe do kraja kolekcije • Metod Current omogućava pristup svakom elementu kolekcije kroz koju se prolazi • Za kreiranje korisničke klase kolekcije, neophodno je implementirati oba pomenuta interfejsa – IEnumerable u klasi kolekcije i enumerator u klasi pokazivača na elemente kolekcije
Type safe IEnumerable i IEnumerator • Osim pomenutih interfejsa – IEnumerable i IEnumerate, postoje i type safe interfejsi IEnumerate<T> i IEnumerable<T> • IEnumerable<T> interfejs deklariše metod GetEnumeratorkoji vraća Enumerator<T>objekat • IEnumerator<T> interface deklariše svojstvo Current koje vraća objekat tipa T • Savetuje se upotreba upravo pomenutih generičkih interfejsa IEnumerable<T> i IEnumerate<T>
Primer za korišćenje enumeratora • Za prethodnu generičku klasu Tree<T> kreiraće se klasa sa objektima enumeratorima koji će služiti za pristup elementima klase Tree preko For each konstrukcije • Za to će se konstruisati nova klasa TreeEnumerator koja implementira IEnumerator interfejs • Definicija / implementacija klase Tree se proširuje sa IEnumerable interfejsom i implementacijom • Klasa Program za testiranje se modifikuje tako što se umesto poziva metoda WalkTree koristi konstrukcija For each
Mali trik u primeru • Da bi se stvari pojednostavile, primenjuje se sledeći trik: • Svi elementi binarnog drveta se u rastućem redosledu učitavaju u kolekciju Queue • Metod populate klase TreeEnumerator realizuje trik, tako što prolazi kroz sve elemente binarnog drveta u rastućem redosledu i ubacuje – Enqueue u kolekciju Queue • Struktura For each onda u stvari prolazi kroz elemente kolekcije Queue koji sadrže elemente binarnog drveta što je sakriveno od korisnika
namespace BinaryTree • { • class TreeEnumerator<T> : IEnumerator<T> where T : IComparable<T> • { • public TreeEnumerator(Tree<T> data) • { • this.currentData = data; • } • private Tree<T> currentData = null; • private T currentItem = default(T); • private Queue<T> enumData = null;
privatevoidpopulate(Queue<T> enumQueue, Tree<T> tree) • { • if (tree.LeftTree != null) • { • populate(enumQueue, tree.LeftTree); • } • enumQueue.Enqueue(tree.NodeData); • if (tree.RightTree != null) • { • populate(enumQueue, tree.RightTree); • } • }
#region IEnumerator<T> Members • T IEnumerator<T>.Current • { • get • { • if (this.enumData == null) • throw new InvalidOperationException("Use MoveNext before calling Current"); • return this.currentItem; • } • } • #endregion
boolSystem.Collections.IEnumerator.MoveNext() • { • if (this.enumData == null) • { • this.enumData = new Queue<T>(); • populate(this.enumData, this.currentData); • } • if (this.enumData.Count > 0) • { • this.currentItem = this.enumData.Dequeue(); • return true; • } • return false; • }
IEnumerable<Titem> Interface • public class Tree<TItem> : IEnumerable<TItem> where TItem : IComparable<TItem> • { • #region IEnumerable<TItem> Members • IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator() • { • return new TreeEnumerator<TItem>(this); • } • #endregion
IEnumerable Interface • #region IEnumerable Members • System.Collections.IEnumeratorSystem.Collections.IEnumerable.GetEnumerator() • { • throw new NotImplementedException(); • } • #endregion • }
class Program • { • static void Main(string[] args) • { • Tree<int> tree1 = new Tree<int>(10); • tree1.Insert(5); • tree1.Insert(11); • tree1.Insert(5); • tree1.Insert(-12); • tree1.Insert(15); • tree1.Insert(0); • tree1.Insert(14); • tree1.Insert(-8); • tree1.Insert(10); • foreach (int item in tree1) • Console.WriteLine(item); • } • }
Enumerator preko Iterator-a • Iterator predstavlja blok koda koji se koristi za pojednostavljenje pristupa kolekciji • Kod iteratora u metodu GetEnumerator se koristi ključna reč yield koja se nalazi unutar tela ciklusa, i sasvim menja smisao ciklusa • Ključna reč yield označava da se radi o iteratoru • Svaki poziv iteratora – izaziva jedan prolazak kroz ciklus, tako da se kroz ciklus prođe tek kada se izređaju svi elementi • Stvarna implementacija je isto kao i bez iteratora, a kod generiše kompajler pri prevođenju • Pojednostavljenje se sastoji u tome da nije potrebna dodatna klasa za objekat enumeratora
using System; • using System.Collections.Generic; • using System.Collections; • class BasicCollection<T> : IEnumerable<T> • { • private List<T> data = new List<T>(); • public void FillList(params T [] items) • { • foreach (var datum in items) • data.Add(datum); • }
IEnumerator<T> IEnumerable<T>.GetEnumerator() • { • foreach (var datum in data) • yield return datum; • } • IEnumerator IEnumerable.GetEnumerator() • { • // Not implemented in this example • } • }
BasicCollection<string> bc = new BasicCollection<string>(); • bc.FillList(“Twas”, “brillig”, “and”, “the”, “slithy”, “toves”); • foreach (string word in bc) • Console.WriteLine(word); • public IEnumerable<T> Reverse • { • get • { • for (int i = data.Count - 1; i >= 0; i--) • yield return data[i]; • } • }
BasicCollection<string> bc = new BasicCollection<string>(); • bc.FillList(“Twas”, “brillig”, “and”, “the”, “slithy”, “toves”); • foreach (string word in bc.Reverse) • Console.WriteLine(word);
Promena primera Tree za iterator • Primer sa klasom Tree se korišćenjem iteratora pojednostavljuje, tako da više nije potrebna dodatna klasa TreeEnumerator za enumerator • Implementacija za metod GetEnumerator()Ienumerable interfejsa se koriguje tako da se koristi yield što označava da se koristi iterator
Promena primera Tree za iterator IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator() { if (this.LeftTree != null) { foreach (TItem item in this.LeftTree) { yield return item; } } yield return this.NodeData; if (this.RightTree != null) { foreach (TItem item in this.RightTree) { yield return item; } } }