Skip to content

Instantly share code, notes, and snippets.

@pjastr
Last active February 4, 2018 17:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pjastr/d94d58dc4ab006698ba2e352e6a63374 to your computer and use it in GitHub Desktop.
Save pjastr/d94d58dc4ab006698ba2e352e6a63374 to your computer and use it in GitHub Desktop.

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.

Własna niegeneryczna kolekcja

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:

k1

Jeśli przed element byłby podany typ Osoba, to wtedy otrzymamy wyjątek:

k2

Własna generyczna kolekcja

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.

Własna kolekcja generyczna (konstrukcja zgodna z SOLID)

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();

Własna kolekcja niegeneryczna (konstrukcja zgodna z SOLID)

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:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment