Ta notatka dotyczy implementacji niegenerycznej wersji interfejsu IEnumerable
. Głównym "zyskiem" z jego implementacji jest tzw. policzalność (przeliczalność, enumerowanie). W praktyce przekłada się na możliwość użycia pętli foreach
. W ogólnym kontekście jest również możliwość modyfikacji kolejności pobierania elementów (choć tego nie będzie na przykładach).
Uwaga: poniższe niektóre przykłady łamią jedną z zasad SOLID, jaką jest zasada jednej odpowiedzialności. Poprawnie powinniśmy rodzielić klasę na pojedyncze byty i kolekcję. Jendak poniżej pokazano, że jest to możliwe w ramach jednej klasy.
Uwaga2: do implementacji interfejsu IEnumerable
potrzemy przestrzeni nazw: System.Collections
. Do poniższego kodu potrzebujemy również przestrzeni nazw: System
, ale wymaga to obsługa metod konsolowych.
Użyjemy klasy Osoba
:
class Osoba : IEnumerable
{
object[] kolekcja = null; //pole na własną kolekcję (niegeneryczna wersja)
int freeIndex = 0; //pole do zliczenia rozmiaru kolekcji
string imie;
string nazwisko;
//konstruktor parametryczny na "pojedynczy" obiekt
public Osoba(string imie, string nazwisko)
{
this.imie = imie;
this.nazwisko = nazwisko;
}
//konstruktor domyślny z inicjacją kolekcji
public Osoba()
{
kolekcja = new object[10];
}
//przesloniecie metody ToString() dla "pojedynczych" obiektów
public override string ToString()
{
return imie + " " + nazwisko; ;
}
//metoda na dodanie czegoś na własną kolekcję
public void Add(object item)
{
kolekcja[freeIndex] = item;
freeIndex++;
}
//implementacja metody z interfejsu
public IEnumerator GetEnumerator()
{
//korzystamy z faktu, że pole kolekcja jest naprawdę tablicą,
// który ma zaimplementowany foreach, możemy w razie potrzeby
// do jednak bardziej urozmaicić
foreach (object o in kolekcja)
{
if (o == null) //sprawdzenie pustych elementów
{
break;
}
yield return o; //tzw. leniwe wyrzucanie
}
}
}
Kod w Main
:
//tworzymy instancję obiektu w typie Osoba
Osoba o1 = new Osoba("Jan","Kowalski");
//tworzymy drugą instancję obiektu w typie Osoba
Osoba o2 = new Osoba("Anna","Nowak");
//tworzymy instancję obiektu na własną kolekcję
Osoba osoby = new Osoba();
osoby.Add(o1);
osoby.Add(o2);
osoby.Add("raz");
osoby.Add(345);
foreach(var element in osoby)
{
Console.WriteLine(element);
}
Console.ReadLine();
Zauważmy, że ponieważ pole kolekcja
w klasie Osoba
nie wymaga konkretnego typu, na listę Osoby
możemy dodawać twory różnych typów. W foreach
możemy wyliczać używając typu var
lub object
. Wtedy na konsoli będzie wynik następujący:
Jeśli przed element
byłby podany typ Osoba
, to wtedy otrzymamy wyjątek:
Kod klasy Osoba
:
class Osoba : IEnumerable
{
Osoba[] kolekcja = null; //pole na własną kolekcję (generyczna wersja)
int freeIndex = 0; //pole do zliczenia rozmiaru kolekcji
string imie;
string nazwisko;
//konstruktor parametryczny na "pojedynczy" obiekt
public Osoba(string imie, string nazwisko)
{
this.imie = imie;
this.nazwisko = nazwisko;
}
//konstruktor domyślny z inicjacją kolekcji
public Osoba()
{
kolekcja = new Osoba[10];
}
//przesloniecie metody ToString() dla "pojedynczych" obiektów
public override string ToString()
{
return imie + " " + nazwisko; ;
}
//metoda na dodanie czegoś na własną kolekcję
public void Add(Osoba item)
{
kolekcja[freeIndex] = item;
freeIndex++;
}
//implementacja metody z interfejsu
public IEnumerator GetEnumerator()
{
//korzystamy z faktu, że pole kolekcja jest naprawdę tablicą,
// który ma zaimplementowany foreach, możemy w razie potrzeby
// do jednak bardziej urozmaicić
foreach (object o in kolekcja)
{
if (o == null) //sprawdzenie pustych elementów
{
break;
}
yield return o; //tzw. leniwe wyrzucanie
}
}
}
Kod w Main
:
//tworzymy instancję obiektu w typie Osoba
Osoba o1 = new Osoba("Jan","Kowalski");
//tworzymy drugą instancję obiektu w typie Osoba
Osoba o2 = new Osoba("Anna","Nowak");
//tworzymy instancję obiektu na własną kolekcję
Osoba osoby = new Osoba();
osoby.Add(o1);
osoby.Add(o2);
foreach(var element in osoby)
{
Console.WriteLine(element);
}
Console.ReadLine();
W tej sytuacji nie dodamy już bytów innego typu (który nie ma jawnego rzutowania). W foreach możemy wymiennie użyć var
, object
i Osoba
.
Dzielimy klasę Osoba
na dwie klasy:
class Osoba
{
string imie;
string nazwisko;
//konstruktor parametryczny na "pojedynczy" obiekt
public Osoba(string imie, string nazwisko)
{
this.imie = imie;
this.nazwisko = nazwisko;
}
//przesloniecie metody ToString() dla "pojedynczych" obiektów
public override string ToString()
{
return imie + " " + nazwisko; ;
}
}
class KolekcjaOsob:IEnumerable
{
Osoba[] kolekcja = null; //pole na własną kolekcję (generyczna wersja)
int freeIndex = 0; //pole do zliczenia rozmiaru kolekcji
//konstruktor domyślny z inicjacją kolekcji
public KolekcjaOsob()
{
kolekcja = new Osoba[10];
}
//metoda na dodanie czegoś na własną kolekcję
public void Add(Osoba item)
{
kolekcja[freeIndex] = item;
freeIndex++;
}
//implementacja metody z interfejsu
public IEnumerator GetEnumerator()
{
//korzystamy z faktu, że pole kolekcja jest naprawdę tablicą,
// który ma zaimplementowany foreach, możemy w razie potrzeby
// do jednak bardziej urozmaicić
foreach (object o in kolekcja)
{
if (o == null) //sprawdzenie pustych elementów
{
break;
}
yield return o; //tzw. leniwe wyrzucanie
}
}
}
Kod w Main
:
//tworzymy instancję obiektu w typie Osoba
Osoba o1 = new Osoba("Jan","Kowalski");
//tworzymy drugą instancję obiektu w typie Osoba
Osoba o2 = new Osoba("Anna","Nowak");
//tworzymy instancję obiektu na własną kolekcję
KolekcjaOsob osoby = new KolekcjaOsob();
osoby.Add(o1);
osoby.Add(o2);
foreach(var element in osoby)
{
Console.WriteLine(element);
}
Console.ReadLine();
Też dzielimy klasę Osoba
na dwie klasy:
class Osoba
{
string imie;
string nazwisko;
//konstruktor parametryczny na "pojedynczy" obiekt
public Osoba(string imie, string nazwisko)
{
this.imie = imie;
this.nazwisko = nazwisko;
}
//przesloniecie metody ToString() dla "pojedynczych" obiektów
public override string ToString()
{
return imie + " " + nazwisko; ;
}
}
class KolekcjaOsob:IEnumerable
{
object[] kolekcja = null; //pole na własną kolekcję (generyczna wersja)
int freeIndex = 0; //pole do zliczenia rozmiaru kolekcji
//konstruktor domyślny z inicjacją kolekcji
public KolekcjaOsob()
{
kolekcja = new object[10];
}
//metoda na dodanie czegoś na własną kolekcję
public void Add(object item)
{
kolekcja[freeIndex] = item;
freeIndex++;
}
//implementacja metody z interfejsu
public IEnumerator GetEnumerator()
{
//korzystamy z faktu, że pole kolekcja jest naprawdę tablicą,
// który ma zaimplementowany foreach, możemy w razie potrzeby
// do jednak bardziej urozmaicić
foreach (object o in kolekcja)
{
if (o == null) //sprawdzenie pustych elementów
{
break;
}
yield return o; //tzw. leniwe wyrzucanie
}
}
}
Kod w Main
:
//tworzymy instancję obiektu w typie Osoba
Osoba o1 = new Osoba("Jan","Kowalski");
//tworzymy drugą instancję obiektu w typie Osoba
Osoba o2 = new Osoba("Anna","Nowak");
//tworzymy instancję obiektu na własną kolekcję
KolekcjaOsob osoby = new KolekcjaOsob();
osoby.Add(o1);
osoby.Add(o2);
osoby.Add("raz");
osoby.Add(345);
foreach(var element in osoby)
{
Console.WriteLine(element);
}
Console.ReadLine();
Bibliografia: