A System.Collection.Generic névtér (Generikusok)
Tartalomjegyzék
Típusai:
– Collection <T> -> Generikus gyűjtemény alapjai.
– Compare<T> -> Két Generikus objektum egyenlőségét vizsgálja.
– Dictionary<TKey, TValue> -> Kulcs/érték párok generikus gyűjteménye.
– List<T> -> Elemek dinamikusan átméretezető listája.
– Queue<T> -> „Elsőnek be, esőnek ki” FIFO lista generikus megvalósítása.
– SortedDictionary<TKey, TValue> -> Rendezett kulcs/érték párok generikus megvalósítása.
– Stack<T> -> „Utolsónak be, elsőnek ki” FIFO-lista generikus megvalósítása.
– LinkedList<T> -> Kétirányú láncolt lista generikus megvalósítása.
– ReadOnlyCollection<T> -> Írásvédett elemek csoportjának generikus megvalósítása.
A List<T> típus vizsgálata:
A System.Collections.Generic.List<T> osztály megköveteli, hogy megadjunk neki egy olyan értéket, amely a List<T> által felhasznált elem típusát írja le. Tehát, ha három List<T> objektumot akarunk létrehozni annak érdekében, hogy megadjunk egészeket, valamint SportCar és Person objektumokat tároljunk, akkor a következőt írhatjuk:
1 2 3 4 5 6 7 8 9 10 11 | static void Main() { //List készítése egész számok tárolására List<int> myInts = new List<int>(); //List készítése SportCar objektumok tárolására List<SportCar> myCars = new List<SportCar>(); //List készítése Person típusú objektumok számára List<Person> myPerson = new List<Person>(); } |
Saját Generikus létrehozása:
A cél az, hogy létrehozzunk egy olyan swap metódus, amely bármely lehetséges adattípuson működik egyetlen paramétertípus használatával. Természetesen a bejövő paraméterek átadása referenciaként történik (C# ref kulcsszó).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Genericethod { class Program { //A metódus megcserél két elemet //a <T> típus paraméternek megfelelően static void Swap<T>(ref T a, ref T b) { Console.WriteLine("Te kuldtel a Swamp metodusnak {0}-(e)t",typeof(T)); T temp; temp = a; a = b; b = temp; } static void Main(string[] args) { Console.WriteLine("Kezdodjon a moka a generikusokkal ;)\n\n"); //két egész szám cseréje int a = 10, b = 90; Console.WriteLine("A csere elott: {0} {1}",a,b); Swap<int>(ref a, ref b); Console.WriteLine("A csere utan: {0} {1}\n", a, b); //Két sztring cseréje string s1 = "Elso", s2 = "Masodik"; Console.WriteLine("A csere elott: {0} {1}", s1,s2); Swap<string>(ref s1, ref s2); Console.WriteLine("A csere utan: {0} {1}", s1, s2); Console.ReadKey(); } } } |
Ugyan ez egy kicsit praktikusabban:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Genericethod { class Program { static void Swap<T>(ref T a, ref T b) { Console.WriteLine("Te kuldel a Swap metodusnak {0}", typeof(T)); Console.WriteLine("Csere elott: {0} {1}",a,b); T temp; temp = a; a = b; b = temp; Console.WriteLine("Csere utan: {0} {1}\n",a,b); } static void Main() { int a = 17, b = 3; Swap<int>(ref a, ref b); string s1 = "Ezt irtam elsonek", s2 = "Ezt meg masodiknak"; Swap<string>(ref s1, ref s2); char c1 = 'a', c2 = 'b'; Swap<char>(ref c1, ref c2); Console.ReadKey(); } } } |
Ezek egy osztályobjektumba tartoznak, viszont természetesen megtehetjük, hogy külön osztályt hozunk létre a Swap metódusnak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Genericethod { class MyGenericMethod { public static void Swap<T>(ref T a, ref T b) { Console.WriteLine("Te kuldel a Swap metodusnak {0}", typeof(T)); Console.WriteLine("Csere elott: {0} {1}", a, b); T temp; temp = a; a = b; b = temp; Console.WriteLine("Csere utan: {0} {1}\n", a, b); } } class Program { static void Main() { int a = 17, b = 3; MyGenericMethod.Swap<int>(ref a, ref b); string s1 = "Ezt irtam elsonek", s2 = "Ezt meg masodiknak"; MyGenericMethod.Swap<string>(ref s1, ref s2); char c1 = 'a', c2 = 'b'; MyGenericMethod.Swap<char>(ref c1, ref c2); Console.ReadKey(); } } } |
Természetesen ezeknek nem kötelező. hogy statikusak legyenek. Egyszerűen létrehozunk egy pédányt és azz hívjuk meg.
var peldany = new MyGenericMethod();
peldany.Swap<…>(ref …, ref …);
Generikus Struktúrák és osztályok:
Ezek segítségével megoldható például, hogy koordinátaként int-et, double-t, float-ot… is megadhassunk neki:
1 2 3 4 5 6 7 | //Egész számokkal működik Point<int> p = new Point<int>(10,15); //double.el működik Point<double> p = new Point<double>(3.466,6.3443); … |
Egy összetettebb példa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Genericethod { public struct Point<T> { T xPos; T yPos; //Generikus konstruktor public Point(T x, T y) { xPos = x; yPos = y; } //Generikus tulajdonságok public T X { get { return xPos; } set { xPos = value; } } public T Y { get { return yPos; } set { yPos = value; } } public override string ToString() { return string.Format("{0}, {1}", xPos, yPos); } //mezők visszaállítása a típusparaméter //alapértelmezett értékére (int-nek(értéktípus) az alapértelmezett //értéke 0, a stringnek(referenciaérték) null public void ResetPoint() { xPos = default(T); yPos = default(T); } } class MainClass { public static void Main() { //Egész számokkal működik Point<int> p = new Point<int>(10, 10); Console.WriteLine("p.ToString = {0}",p.ToString()); p.ResetPoint(); // Itt lenullázzuk az értéket (mivel értéktípusnál ez az alapértelmezett) Console.WriteLine("p.ToString() = {0}",p.ToString()); Console.ReadKey(); //double számokkal működik Point<double> p2 = new Point<double>(5.4, 3.3); Console.WriteLine("\np2.ToString() = {0}",p2.ToString()); p2.ResetPoint(); // Itt lenullázzuk az értéket (mivel értéktípusnál ez az alapértelmezett) Console.WriteLine("p2.ToString() = {0}", p2.ToString()); Console.ReadKey(); } } } |
Saját generikus gyűjtemény készítése:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | using System; using System.Collections; using System.Collections.Generic; namespace CustomGenericCollection { public class Car { public string PetName; public int Speed; public Car(string name, int currentSpeed) { PetName = name; Speed = currentSpeed; } public Car() { } } public class SportsCar : Car { public SportsCar(string p, int s) : base(p, s) { } } public class MiniVan : Car { public MiniVan(string p, int s) : base(p, s) { } } public class CarCollection<T> : IEnumerable<T> { private List<T> arCars = new List<T>(); public T GetCar(int pos) { return arCars[pos]; } public void AddCar(T c) { arCars.Add(c); } public void ClearCars() { arCars.Clear(); } public int Count { get { return arCars.Count; } } //Az IEnumerable<T> bővíti az IEnumerable interfészt. //ezért a GetEnumerator() mindkét verzióját meg kell valósítani. IEnumerator<T> IEnumerable<T>.GetEnumerator() { return arCars.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return arCars.GetEnumerator(); } } class MainClass { public static void Main() { //objektumok létrehozása CarCollection<Car> myCars = new CarCollection<Car>(); myCars.AddCar(new Car("Rusty", 20)); myCars.AddCar(new Car("Zippy", 90)); foreach (var c in myCars) { Console.WriteLine("PetName: {0}, Speed: {1}", c.PetName, c.Speed); } Console.ReadKey(); } } } |
Generikusok
A generikusok szimpla használata
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using System; using System.Collections; using System.Collections.Generic; namespace gyakorlás { class SwapClass { public void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } } class MainClass { public static void Main() { var swap = new SwapClass(); int a = 10, b = 20; string s1 = "Hiba", s2 = "lesz"; //swap.Swap<int>(ref a, ref b); Console.WriteLine("{0}, {1}", a, b); swap.Swap<string>(ref s1, ref s2); Console.WriteLine("{0}, {1}", s1, s2); Console.ReadKey(); } } } |
mellett lehetőségünk van még rengeteg opcióra. A megszorításokon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | using System; using System.Collections; using System.Collections.Generic; namespace gyakorlás { class SwapClass { public void Swap<T>(ref T a, ref T b) where T : struct { T tmp = a; a = b; b = tmp; } } class MainClass { public static void Main() { var swap = new SwapClass(); int a = 10, b = 20; string s1 = "Hiba", s2 = "lesz"; //swap.Swap<int>(ref a, ref b); Console.WriteLine("{0}, {1}", a, b); //Ez nem működik, mivel csak értéktípusokat fogadhat, ha a class //kulcsszó lenne a struct helyett, akkor lenne érvényes ez! //swap.Swap<string>(ref s1, ref s2); //Console.WriteLine("{0}, {1}", s1, s2); Console.ReadKey(); } } } |
keresztül az előredefiniált generikus interfészekig…
Mivel a generikusok bármilyen típust fogadhatnak, nem lehet operátorokat használni. Gondoljunk csak bele
1 2 3 4 | public T Swap<T>(ref T a, ref T b) { return a + b; } |
Ez a példa nem fordul le! De mért nem? Hozzáadunk, valamilyen értéktípust (int, double, float…) például 5-öt meg 6-ot akkor mért nem tér vissza 11-el? Azért nem, mert referenciatípusnál nincs értelme operátor használatának. Mivel a rendszer gondol arra is, hogy akár stringet is hozzáadhatunk ezért nem fordul le! (Magyarul nincs értelne, hogy pl.: Pista + Sándor, vagy asd – dsa…)
Generikus ősosztályok
Ha generikus osztályból származtatunk, akkor öröklésnél meg kell határoznunk, hogy milyen típust fogunk visszaadni a generikusnak.
1 2 3 4 5 6 7 8 9 10 | public class MyList<T> { List<T> listOfData = new List<T>(); } //a konkrét típusoknak meg kell határoznia a típus //paramétert, ha generikus osztály leszármazottjai public class myStrigList : MyList<string> { } |
Ugyan ez a helyzet felüldefiniálásoknál.
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class MyList<T> { List<T> listOfData = new List<T>(); public virtual void PrintData(T data) { } } //a konkrét típusoknak meg kell határoznia a típus //paramétert, ha generikus osztály leszármazottjai public class myStrigList : MyList<string> { public override void PrintData(string data) { } } |
Ezt viszont meg tudja helyettünk csinálni a visual studio! Leírjuk hogy: public override void és ekkor megjelenik az intellisense abból kiválasztjuk a kérdéses metódust, és autómatikusan felüldefiniálja nekünk a régit.
Generikus interfészek
Már volt szó róla, hogy operátorokat nem használhatunk generikusoknál bizonyos okokból. Ezt ki lehet küszöbölni a megszorítások és interfészek használatával:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | using System; using System.Collections.Generic; using System.Text; namespace GenericInterface { #region A generic interface public interface IBinaryOperations<T> where T : struct { T Add(T arg1, T arg2); T Subtract(T arg1, T arg2); T Multiply(T arg1, T arg2); T Divide(T arg1, T arg2); } #endregion #region Implementation of generic interface. public class BasicMath : IBinaryOperations<float> { public float Add(float arg1, float arg2) { return arg1 + arg2; } public float Subtract(float arg1, float arg2) { return arg1 - arg2; } public float Multiply(float arg1, float arg2) { return arg1 * arg2; } public float Divide(float arg1, float arg2) { return arg1 / arg2; } } #endregion class Program { static void Main(string[] args) { Console.WriteLine("***** Generic Interfaces *****\n"); BasicMath m = new BasicMath(); Console.WriteLine("1.98 + 1.3 = {0}", m.Add(1.98F, 1.3F)); Console.ReadLine(); } } } |