# C# Genrics (2/18~2/20) ###### tags: `Generics` `constraints` `note` `ASP.NET` ## Generics 的使用精神 - 在實作(instance)前,先不宣告型態 > Generics let you tailor a method, class, structure, or interface to the precise data type it acts upon. [2] ### Generic Methods 範例1 ```csharp public static bool CheckType<T1, T2>(T1 foo, T2 bar) { return foo.GetType() == bar.GetType(); } public class TestType1 {} public class TestType2 {} ``` ```csharp Console.WriteLine(@"Compare ""123"" to 123"); Console.WriteLine(CheckType("123", 123)); TestType1 obj1 = new TestType1(); TestType2 obj2 = new TestType2(); Console.WriteLine(@"Compare obj1 to obj2"); Console.WriteLine(CheckType(obj1, obj2)); TestType1 obj3 = new TestType1(); Console.WriteLine(@"Compare obj1 to obj3"); Console.WriteLine(CheckType(obj1, obj3)); /* Output: Compare "123" to 123 False Compare obj1 to obj2 False Compare obj1 to obj3 True */ ``` ## Generic Classes > Generic classes encapsulate operations that are not specific to a particular data type. The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees, and so on. \[4] 常用來實作各種資料結構或演算法 ### Generic Class 範例1 - Dictionary \[5] Ref[5]雖說他實作的是HashTable,但實際上因為它使用了Generics,他的Table於建立時就固定了Key與Value的DataType,所以他實作的應該為Dictionary而非HashTable。 而HashTable的寫法如下,取消Class HashTable的泛型,並將內容物的DataType改成dynamic即可。 ```csharp public struct KeyValue<K, V> { public K Key { get; set; } public V Value { get; set; } } public class FixedSizeGenericHashTable { private readonly int size; private readonly LinkedList<KeyValue<dynamic,dynamic>>[] items; public FixedSizeGenericHashTable(int size) { this.size = size; items = new LinkedList<KeyValue<dynamic,dynamic>>[size]; } protected int GetArrayPosition(dynamic key) { int position = key.GetHashCode() % size; return Math.Abs(position); } public dynamic Find(dynamic key) { int position = GetArrayPosition(key); LinkedList<KeyValue<dynamic, dynamic>> linkedList = GetLinkedList(position); foreach (KeyValue<dynamic,dynamic> item in linkedList) { if (item.Key.Equals(key)) { return item.Value; } } return default(dynamic); } public void Add(dynamic key, dynamic value) { int position = GetArrayPosition(key); LinkedList<KeyValue<dynamic, dynamic>> linkedList = GetLinkedList(position); KeyValue<dynamic, dynamic> item = new KeyValue<dynamic, dynamic>() { Key = key, Value = value }; linkedList.AddLast(item); } public void Remove(dynamic key) { int position = GetArrayPosition(key); LinkedList<KeyValue<dynamic, dynamic>> linkedList = GetLinkedList(position); bool itemFound = false; KeyValue<dynamic, dynamic> foundItem = default(KeyValue<dynamic, dynamic>); foreach (KeyValue<dynamic,dynamic> item in linkedList) { if (item.Key.Equals(key)) { itemFound = true; foundItem = item; } } if (itemFound) { linkedList.Remove(foundItem); } } protected LinkedList<KeyValue<dynamic, dynamic>> GetLinkedList(int position) { LinkedList<KeyValue<dynamic, dynamic>> linkedList = items[position]; if (linkedList == null) { linkedList = new LinkedList<KeyValue<dynamic, dynamic>>(); items[position] = linkedList; } return linkedList; } } ``` ## Features of Generics \[7] - It helps you in code reuse, performance and **type safety**. \ 例如Dictionary雖沒有固定Key Value的DataType,但在Dictionary一實作(new)後,Key Value的DateType就被固定了 - You can create your own generic classes, methods, interfaces and delegates. \ 可以把delegate想成methods版的interface - You can create generic **collection classes**. \ 例如\[5]中的 LinkedList<KeyValue<K,V>> ## where T : constraint \[8] 在使用Generics時,雖說未指定的DataType\<T>非常方便,但在Methods或Classes中要使用\<T>的物件下的一些Properties或Methods時,又會變得無法保證該DataType下一定有這些內容。 \ 此時利用where T : constraint可以進一步限制or保護該Generics Classes/Methods。 ### where T : class MSDN描述如下 `where T : class` > The type argument must be a reference type. This constraint applies also to any class, interface, delegate, or array type. In a nullable context in C# 8.0 or later, T must be a non-nullable reference type. pass by ref 除了可以在讀入較大的物件時省時間空間,實在很難想到其他好處= =。 如果要發揮直接修改該物件的優點,應該還要進一步限制T的DataType才對。 ### where T : userdefine class 範例1 想像有一個車廠,專門組合 固定廠牌的車用零件 與 固定廠牌的車子控制系統,那我們就可以Generic class搭配constraint宣告一個CarFactory如下。 之後利用已宣告好的Honda_Body與Tesla_Driverless來測試。 ```csharp public interface ICarComponent { } public interface ICarControler { } public class CarFactory<T1,T2> where T1 : ICarComponent where T2 : ICarControler { public LinkedList<T1> CarComponents { get; set; } public LinkedList<T2> CarControlers { get; set; } public CarFactory() { this.CarComponents = new LinkedList<T1>(); this.CarControlers = new LinkedList<T2>(); } } public class Honda_Body : ICarComponent { } public class Tesla_Driverless : ICarControler { } ``` ```csharp CarFactory<Honda_Body,Tesla_Driverless> plant1 = new CarFactory<Honda_Body, Tesla_Driverless>(); Honda_Body model1 = new Honda_Body(); Tesla_Driverless truckModel = new Tesla_Driverless(); Tesla_Driverless vanModel = new Tesla_Driverless(); plant1.CarComponents.AddLast(model1); plant1.CarControlers.AddLast(truckModel); plant1.CarControlers.AddLast(vanModel); ``` 上面程式碼將 1個Honda的車體零件 與 2個特斯拉的無人駕駛模組 加入該車廠的組合選擇。 想像其內部有另外一個Assemble的Method可以根據 零件(ICarComponent)與 控制系統(ICarControler)來Return不同功能的車輛,此時where的限制就可以**確保**我們在Assemble時可以從特定class找到我們需要的資訊或功能。 MSDN還有很多不同constraint的例子,但沒有需求的話有點難體會他限制帶來的好處= =,感覺都可以直接用interface來限定DataType取代吧(?) ### 心得 1. HashTable對應到LinkedList可以在HashCode collision後繼續以Key利用foreach key in LinkedList查找。 2. Generic Class中的properties才能用generic type。 3. 於宣告時加了 readonly keyword 的 property 限制了一定要加readonly keyword才能修改property (Constructor中可不用加)。 ## 參考資料 \[1] 陳明山,江通儒,ASP.NET Core 打造 軟體積木和應用系統 \ \[2] [C# generics](https://docs.microsoft.com/en-us/dotnet/standard/generics/) \ \[3] [Generic Methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-methods) \ \[4] [Generic Classes](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-classes) \ \[5] [example of a hashtable in C#](https://stackoverflow.com/questions/625947/what-is-an-example-of-a-hashtable-implementation-in-c) \ \[6] [Basics of Hash Tables](https://www.hackerearth.com/practice/data-structures/hash-tables/basics-of-hash-tables/tutorial/) \ \[7] [C# | Generics – Introduction](https://www.geeksforgeeks.org/c-sharp-generics-introduction/) \ \[8] [Constraints on type parameters](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters)