недеља, 05. јун 2016.

Šta su generici i kako se prave?


Pod pojmom generici u većini slučajeva se podrazumevaju sve klase koje sadrži imenski prostor System.Collection.Generic. Često se generičke klase, generičke liste ili uopšteno sve generičke kolekcije svrstavaju pod pojmom generici, ali za iskusnije programere kad kažete generici to se uglavnom misli na generičke klase, metode, strukture i sve što zahteva parametre. Znači češće se misli na nešto što ste vi sami isprogramirali da bude generičko dok pod pojmom generičke liste se misli na generičke liste koje već postoje u imenskom prostoru System.Collection.Generic. U prethodnom postu „Negeneričke kolekcije u C# programskom jeziku“ sam objasnio šta su negeneričke kolekcije, koje postoje u System.Collection imenskom prostoru i dao sam vam jednostavan primer kako da pravite vlastitu negeneričku klasu iako se takve retko prave. Takođe sam vam naveo da su generičke kolekcije za razliku od negenerički kolekcija; one kolekcije koje su parametorizovane; znači moraju imati određen tip podataka i svi elementi u kolekciji moraju biti istog tipa podataka.


( Generici u C# programskom jeziku )


Jednostavno, generičke kolekcije koriste se kad želite da svi elementi u kolekciji budu istog tipa podataka i zato su type safe. Izuzetno su korisni kada želite da izbegnete hard kodirane tipove i ne morate da pamtite koja vrsta podataka se koristi. Takođe nemate potrebe ni za eksplicitnom konverzijom. To se sve odnosi i na generike i na generičke liste i uopšteno na sve generičke kolekcije. Inače pod pojmom generici ću se isključivo baviti pravljenjem vlastitih generički kolekcija. Vi ćete u vašim programima često koristiti već napravljene generičke liste iz klasa .Net-a, ali će te takođe često dolaziti u situaciju da sami pravite svoje generike. Još samo jedna napomena. Ukoliko pravite igrice, korišćenje uopšteno svih kolekcija se smatra skupo, uzima više memorije i može usporiti igricu, pa u tom slučaju preporučujem da izbegavate kolekcije iako programerima olakšavaju posao. Inače u poslovnim aplikacijama, kolekcije se koriste konstantno ali generičke kolekcije. Kada pokrenete Microsoft Visual Studio, primetiće te da se imenski prostor System.Collection.Generic insertuje u vašim aplikacijama dok to nije slučaj sa imenskim prostorom System.Collection. To vam je takođe znak da treba da koristite generičke kolekcije ako već koristite kolekcije.

Generička klasa sa jednim parametrom


Generički tipovi se deklarišu šiljatim zagradama < > iza kojih se navodi ime tipa. Praksa je da između zagrada za tip koristite slovo T ukoliko radite sa jednim parametrom ili da koristite slovo T kao  predznak poput TKey, TValue ukoliko deklarišete više parametara. Pogledajte jednostavan primer kako se pravi generička klasa koja zahteva samo jedan parametar i kako se ista koristi.

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

namespace GenericClassWithOneParameter
{
    class GenericClass <T>
    {
        private T value;

        public GenericClass(T t)
        {
            this.value = t;

        }

        public void Print()
        {
            System.Console.WriteLine($"Value: {this.value}");
            System.Console.WriteLine($"Type:  {typeof(T).ToString()}" + Environment.NewLine);

        }
    }
}

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

namespace GenericClassWithOneParameter
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("1. Use GenericClass class with integer type." + Environment.NewLine);

            GenericClass<int> test1 = new GenericClass<int>(1250);
            test1.Print();

            WriteLine("2. Use GenericClass class with double type." + Environment.NewLine);

            GenericClass<double> test2 = new GenericClass<double>(999.99);
            test2.Print();

            WriteLine("3. Use GenericClass class with string type." + Environment.NewLine);

            GenericClass<string> test3 = new GenericClass<string>("Hello from the Generic class!");
            test3.Print();

            WriteLine("Press any key to continue...");
            ReadKey();

        }
    }
}

Kao što možete primetiti u navedenom kodu; zahvaljujući tome što ste klasu napravili generičkom, vaša klasa ne mora da ima posebno napisane metode za svaki tip podataka koji će je koristiti. Jednostavno, pozivate vašu generičku klasu sa koji god hoćete tipom podataka, stime da naravno za svaku instancu klase morate unapred da odredite sa kojim tipom podataka radite. Pretpostavljam da bar delimično možete naslutiti kolika je ovo benefit za kodiranje. Kad pokrenete navedeni kod, vaš rezultat će biti sledeći:

1. Use GenericClass class with integer type.



Value: 1250

Type:  System.Int32



2. Use GenericClass class with double type.



Value: 999.99

Type:  System.Double



3. Use GenericClass class with string type.



Value: Hello from the Generic class!

Type:  System.String



Press any key to continue...

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



( C# 6.0 Tutorial - Advanced - 30. Generic Class With One Parameter )

Generička klasa sa više parametara

Sa koliko tipova parametara i kojih tipova parametara će te koristiti neku generičku klasu, zavisi samo od toga kako ste generičku klasu definisali pre nego što ste je napravili. Sledeći primer ilustruje kako da napravite generičku klasu sa dva parametra. Vi sami birate kog tipa će biti parametri, stime da i prvi i drugi parametar mogu imati različite tipove, po vašoj želji. Nemojte ovaj jednostavan primer koji samo ilustruje delimičnu primenu generičke klase sa više parametara mešati sa rečnicima.

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

namespace GenericClassWithMultipleParameters
{
    class GenericClass<TKey, TValue>
    {
        private TKey key;
        private TValue value;

        public void SetKey(TKey Key) => key = Key;

        public TKey GetKey() => key;

        public void SetValue(TValue Value) => value = Value;

        public TValue GetValue() => value;

        public void Print(TKey key, TValue value)
        {
            System.Console.WriteLine($"Your key is: {key} and it is {key.GetType().ToString()} type.");
            System.Console.WriteLine($"Your value is: {value} and it is {value.GetType().ToString()} type." + Environment.NewLine);

        }
    }
 }

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

namespace GenericClassWithMultipleParameters
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Employeed:" + Environment.NewLine);

            GenericClass<int, string> Employee = new GenericClass<int, string>();
            Employee.SetKey(1025);
            Employee.SetValue("Manuel Radovanovic");
            Employee.Print(Employee.GetKey(), Employee.GetValue());

            WriteLine("Department:" + Environment.NewLine);

            GenericClass<string, string> Department = new GenericClass<string, string>();
            Department.SetKey("Senior Programmer 17");
            Department.SetValue("Development Team 4");
            Department.Print(Department.GetKey(), Department.GetValue());

            WriteLine("Press any key to continue...");
            ReadKey();

        }
    }
}

U primeru su korišćeni lambda izrazi, jednostavno da se privikavate da se metode mogu pisati na kraći i jednostavniji način. Takođe, primer pokazuje da obadva parametra mogu biti različiti i da možete za parametre koristiti koje god želite tipove podataka, stime da prilikom instanciranja morate da ih navedete. Kad ovaj program pokrenete, rezultati će biti sledeći:

Employeed:

Your key is: 1025 and it is System.Int32 type.
Your value is: Manuel Radovanovic and it is System.String type.

Department:

Your key is: Senior Programmer 17 and it is System.String type.
Your value is: Development Team 4 and it is System.String type.

Press any key to continue...


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



( C# 6.0 Tutorial - Advanced - 31. Generic Class With Multiple Parameters )

Šta su generički tipovi?

Kada vas neko pita za generičke tipove, to nisu sad neki posebni tipovi u programiranju već se to u stvari odnosi na Generic Constraints – generička ograničenja. Na generičke parametre se mogu primeniti razna ograničenja u slučajevima kad se traži veća specifikacija tipa. Ukoliko imate više parametara sa ograničenjem, tada za svaki parametar morate navesti ključnu reč where.

where T : ime_klase – T nasleđuje zadatu klasu
 

where T : ime_interfejsa – T implementira zadati interfejs
 

where T : class – T mora biti klasa ili neki referenti tip

where T : struct – T mora biti struktura ili neki vrednosni tip

where T : new () – T mora imati javni konstruktor bez parametara

where T : U – T je izveden iz drugog U generičkog parametra


Pogledajte sledeći primer koji implementira zadati interfejs kako bi olakšao izračunavanje maksimalni i minimalni troškova.

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

namespace GenericConstraintsInterface
{
    class GenericClass <T> where T:IComparable
    {
        public T MaximumCosts(T FirstValue, T SecondValue)
        {
            T Maximum = FirstValue;

            if (SecondValue.CompareTo(FirstValue) > 0) Maximum = SecondValue;

            return Maximum;
        }

        public T MinimumCosts(T FirstValue, T SecondValue)
        {
            T Minimum = FirstValue;

            if (SecondValue.CompareTo(FirstValue) < 0) Minimum = SecondValue;

            return Minimum;

        }
    }
}

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

namespace GenericConstraintsInterface
{
    class Program
    {
        static void Main(string[] args)
        {
            GenericClass<double> Costs = new GenericClass<double>();

            double[] products = { 154.99, 188.63 };

            double max = Costs.MaximumCosts(products[0], products[1]);
            double min = Costs.MinimumCosts(products[0], products[1]);

            WriteLine("Costs..." + Environment.NewLine);

            WriteLine($"First costs: {products[0]}");
            WriteLine($"Second costs: {products[1]}" + Environment.NewLine);

            WriteLine($"Maximum costs: {max.ToString()}");
            WriteLine($"Minimum costs: {min.ToString()}");

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }
    }
}

Kad pokrenete navedeni program rezultat će biti sledeći:

Costs...

First costs: 154.99
Second costs: 188.63

Maximum costs: 188.63
Minimum costs: 154.99

Press any key to continue...


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



( C# 6.0 Tutorial - Advanced - 32. Generic Constraints Interface )

Kako se prave generičke metode?

Metode mogu da budu generičke i to bez obzira da li je klasa u kojoj je nalaze generička, jednostavno generička metoda je metoda koja je deklarisana sa generičkim parametrima. Tačno je da u C++ programskom jeziku generička metoda ne može da bude virtualna, ali u C# programskom jeziku može. Ono što ne možete jeste da nadjačate generički parametar iz izvedene klase. Postoji dosta situacija gde će te nailaziti na kompleksnu upotrebu generički metoda. Za sada pogledajte jednu jednostavnu upotrebu generičke metode u sledećem primeru:

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

namespace GenericMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            string FirstName = "Manuel";
            string LastName = "Radovanovic";

            WriteLine("In USA people write first name before last name:" + Environment.NewLine);
            Print(FirstName, LastName);

            WriteLine("In Serbia people write last name before first name more often:" + Environment.NewLine);
            Swap<string>(ref FirstName, ref LastName);
            Print(FirstName, LastName);
           
            WriteLine("Press any key to continue...");
            ReadKey();

        }

        static void Print(string left, string right)
        {
            WriteLine(left + " " + right + Environment.NewLine);

        }
        static void Swap<T>(ref T left, ref T right)
        {
            T temp;
            temp = left;
            left = right;
            right = temp;
                  
        }
    }
}

Kad pokrenete navedeni program rezultat će biti sledeći:

In USA people write first name before last name: 

Manuel Radovanovic

In Serbia people write last name before first name more often:

Radovanovic Manuel

Press any key to continue...


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


( C# 6.0 Tutorial - Advanced - 33. Generic Method )