субота, 27. фебруар 2016.

Indekseri u C# programskom jeziku



Indekser vam je jedna posebna vrsta propertija koja vam omogućava da indeksirate strukturu ili klasu kao niz. I pored toga što indekseri ne predstavljaju esencijalne aspekte objektno orjentisanog programiranja, omogućava vam sintaksnu pogodnost za korišćenje struktura i klasa na intuitivniji način. Jednostavno vam omogućavaju da pristupate objektu kao da je u pitanju niz. Indekseri se čak mogu pozivati i u interfejsu. Iskusni C++ programeri treba da imaju u vidu da indekseri u C# programskom jeziku imaju istu svrhu kao i nadjačavanje operatora [ ] u C++ programskom jeziku.






( Indekseri su posebna vrsta propertija i funkcionišu drugačije od propertija )


Indekser uvek počinjete da pišete kao metodu ali umesto naziva metode pišete ključnu reč this. Međutim, indekser nije metoda, ne koristite male zagrade ( ) za parametre već uglaste zagrade [ ] koje navode indeks. Zatim se indekser nastavlja pisati kao properti i takođe sadrži pristupne metode get i set.

Element tip this[ tip indeks]
{
         get
        {

            // vraća specifičnu vrednost preko indeksa

        }  

        set 
       {

          // setuje specifičnu vrednost preko indeksa

       } 

}

U informatičkoj literaturi ćete možda čitati da u jednoj strukturi i klasi možete imati samo jedan indekser što je tačno, ali treba da znate da uvek možete preklopiti indekser ili imati više njegovih implementacija. Jednostavno indekseri se mogu kodirati sa različitim parametrima i ne moraju biti samo tipa integer. Indekseri se uglavnom koriste za kolekcije, liste, rečnike i sve što zahteva indeksiranje. Najbolji način da razumete indeksiranje je da analizirate programerski kod; gde god se indekseri upotrebljavaju i nemojte da vas zbuni njihova raznovrsna upotreba.

Kako izgleda praktični primer upotrebe indeksera? 
 
Da bi programerski primer upotrebe indeksera bio što smisleniji i da bi ste imali veći uvid kako se koriste indekseri ja ću u ovom primeru izbeći najjednostavniji prikaz indeksera, već ću koristiti i neke stvari o kojima još nisam pisao na ovom blogu. Tako da neki deo koda više shvatite kao uvod u nešto o čemu će se kasnije pisati kad dođem do toga. Prvo pravimo najjednostavniju klasu Student bez komplikovanja i konstruktora. Potrebni su nam samo propertiji.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Indexers
{
    class Student
    {
        public int StudentID { get; set; }
        public string FullName { get; set; }
        public string Gender { get; set; }
        public byte Age { get; set; }
   
    }
}

Kao što vidite vrlo jednostavno. Posle navedene klase pravimo klasu University koja će da koristi klasu Student kao tip podataka. Tačnije definisaćemo i kreirati jednu generičku listu u koju ćemo smestiti 5 studenata. Za sada ne morate da znate šta su generičke liste, jednostavno ih za sada shvatite kao neku vrstu naprednijeg niza.

private List<Student> listStudents;

Ovako deklarisana lista vam jednostavno znači da lista studenata će sadržavati objekte tipa Student. Zatim ćemo umesto podataka u nekoj datoteci ili bazi podataka, jednostavno smestiti podatke o studentima u našu listu u konstruktoru klase University. Obratite pažnju kako se to radi.

public University()
        {
            listStudents = new List<Student>();

            listStudents.Add(new Student { StudentID = 1, FullName = "Manuel Radovanovic", Gender = "Male", Age = 40 });
            listStudents.Add(new Student { StudentID = 2, FullName = "Burton Skinner", Gender = "Male", Age = 45 });
            listStudents.Add(new Student { StudentID = 3, FullName = "Erica Hopper", Gender = "Female", Age = 36 });
            listStudents.Add(new Student { StudentID = 4, FullName = "Matthew Robinson", Gender = "Male", Age = 24 });
            listStudents.Add(new Student { StudentID = 5, FullName = "Allison Harvey", Gender = "Female", Age = 24 });

        }

Sad i sami možete videti da punjenje generičke liste studenata nije ništa toliko komplikovano iako ne znate ništa o generičkim listama. Jednostavno koristite naredbu Add i unesete vrednosti svakog objekta Student za svakog studenta pojedinačno. I sad kad imamo listu studenata, onda imamo i povod da pravimo indeksere.

public string this[int StudentID]
        {
            get
            {
                return listStudents.FirstOrDefault(st => st.StudentID == StudentID).FullName;

            }
            set
            {
                listStudents.FirstOrDefault(st => st.StudentID == StudentID).FullName = value;

            }

        }

Šta je sad ovo? Ovo vam je jednostavan indekser koji preko ID studenta vraća ili menja ime i prezime studenta. Kod koji to omogućava je pisan u stilu LINQ upita. Za sada ne morate znati  LINQ upite, već jednostavno obratite pažnju da se navedeni indekser sastoji od pristupni metoda get i set. Na ovaj način vi prosleđujete indekseru ID studenta i na osnovu toga indekser vam vraća ime i prezime studenta. Pristupna metoda set vam omogućava i da promenite ime i prezime studenta na osnovu indeksa. Posle navedenog indeksera, pravimo ponovo isti indekser ali mu menjamo atribute inače bi nam kompajler prijavio grešku.

public string this[string Gender] => listStudents.Count(st => st.Gender == Gender).ToString();

Nemojte da vas zbuni navedeno pisanje indeksera. Od verzije C# 6.0 vi možete i indeksere da kodirate u kraćem obliku. Imajte na umu samo jednu stvar, da navedena skraćena implementacija sadrži samo pristupnu metodu get bez pristupne metode set. Navedena implementacija indeksera nam omogućava da izbrojimo koliko u listi studenata ima muškaraca ili devojaka u zavisnosti kakvu vrednost smo poslali indekseru. To je sve. Zahvaljujući indekseru, vi možete iz metode Main vašu klasu da pozivate i koristite kao da radite sa nizom.

University university = new University();
         
for (byte i = 1; i < 6; i++)
{
WriteLine(university[i]);

}

Zatim, kad hoćete da vidite koliko na listu studenata imate muškaraca i koliko imate devojki jednostavno pozivate istu klasu kao niz i dodeljujete joj vrednost za atribut Gender:

WriteLine("Total Male Students: " + university["Male"]);
WriteLine("Total Female Students: " + university["Female"]);

Nadam se da ste sad bolje shvatili zašto i kako se mogu koristiti indekseri. Navedenoj klasi vi možete navesti još implementacija istog indeksera prema vašim potrebama. Pogledajte kako izgledao celi programerski kod programa.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Indexers
{
    class Student
    {
        public int StudentID { get; set; }
        public string FullName { get; set; }
        public string Gender { get; set; }
        public byte Age { get; set; }
   
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Indexers
{
    class University
    {
        private List<Student> listStudents;

        public University()
        {
            listStudents = new List<Student>();

            listStudents.Add(new Student { StudentID = 1, FullName = "Manuel Radovanovic", Gender = "Male", Age = 40 });
            listStudents.Add(new Student { StudentID = 2, FullName = "Burton Skinner", Gender = "Male", Age = 45 });
            listStudents.Add(new Student { StudentID = 3, FullName = "Erica Hopper", Gender = "Female", Age = 36 });
            listStudents.Add(new Student { StudentID = 4, FullName = "Matthew Robinson", Gender = "Male", Age = 24 });
            listStudents.Add(new Student { StudentID = 5, FullName = "Allison Harvey", Gender = "Female", Age = 24 });

        }

        public string this[int StudentID]
        {
            get
            {
                return listStudents.FirstOrDefault(st => st.StudentID == StudentID).FullName;

            }
            set
            {
                listStudents.FirstOrDefault(st => st.StudentID == StudentID).FullName = value;

            }

        }

        public string this[string Gender] => listStudents.Count(st => st.Gender == Gender).ToString();
                     
    }
}

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Indexers
{
    class Program
    {
        static void Main(string[] args)
        {
            University university = new University();

            WriteLine("All students at the univesity:");
            WriteLine(new String('-', 20));

            for (byte i = 1; i < 6; i++)
            {
                WriteLine(university[i]);

            }

            WriteLine(new String('-', 20));

            WriteLine("Changed the name of Index 2:");
            university[2] = "Bill Gates";
            WriteLine(university[2]);

            WriteLine(new String('-', 20));

            WriteLine("Total Male Students: " + university["Male"]);
            WriteLine("Total Female Students: " + university["Female"]);
           
            ReadKey();

        }
    }

Kad pokrenete navedeni program rezultati će biti isti.

All students at the univesity:
--------------------
Manuel Radovanovic
Burton Skinner
Erica Hopper
Matthew Robinson
Allison Harvey
--------------------
Changed the name of Index 2:
Bill Gates
--------------------
Total Male Students: 3
Total Female Students: 2

Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Fundamentals - 42. Indexers )