C# Generikusok

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:

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

HOZZÁSZÓLOK A CIKKHEZ

Kérjük, írja be véleményét!
írja be ide nevét