# 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)