A System.Collection.Generic névtér (Generikusok)
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:
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ó).
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:
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:
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:
//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:
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:
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
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
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
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.
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.
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:
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();
}
}
}
Harder
Profil