# Generic

## 😢 ปัญหา

ในบางครั้งที่เราเขียนโค้ด เราก็อาจจะไม่รู้ก็ได้ว่าเจ้าตัวแปรตัวนี้มันควรจะมี Data type เป็นอะไรดี เพราะมันขึ้นอยู่กับว่าคนที่เรียกใช้มันจะส่งอะไรมาให้นั่นเอง&#x20;

**ตัวอย่าง:** เราอยากมีคลาสที่เอาไว้**เก็บข้อมูลอะไรก็ได้ไว้ 1 ตัว** ซึ่งตอนแรกอาจจะเก็บค่าตัวเลขไว้ ก็จะได้โค้ดออกมาแบบนี้

```csharp
public class SimpleStorage
{
    public int Value;
}
```

คำถามคือ แล้วถ้าเราอยากให้คลาสนั้นมันเก็บ double เข้าไปบ้างล่ะ หรืออาจจะเก็บ string บ้างล่ะ เราจะทำยังไงดี ?

{% hint style="danger" %}
**คำเตือน**\
เพื่อนๆบางคนอาจจะบอกว่า งั้นก็กำหนดให้มันเป็น **object** ไปดิ จะเก็บอะไรก็เก็บได้หมดเบย!! คำตอบคือใช่ครับทำแบบนั้นก็ทำงานได้ แต่โค้ดแบบนั้นมันจะทำให้ performance ตกลง เพราะเราต้องไปแปลงของต่างๆให้ไปเป็น object และ ตอนที่เราจะเอาค่ามันกลับมา เราก็ต้องทำการแปลงกลับมาด้วย ซึ่งเราเรียกเรื่องนี้ว่าการทำ [Boxing and Unboxing](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing) **แนะนำว่าอย่าทำ**
{% endhint %}

{% hint style="info" %}
**แนะนำให้อ่าน**\
การทำ Boxing and Unboxing นั้นจริงๆมันเป็นยังไง สามารถอ่านได้จากบทความนี้เลย [💡 Boxing & Unboxing](https://saladpuk.gitbook.io/learn/beginner-1/csharp101/advanced/boxing-and-unboxing)
{% endhint %}

## 😄 วิธีแก้ปัญหา

เราสามารถใช้สิ่งที่เรียกว่า **Generic** มาช่วยแก้ปัญหาที่ว่าได้ โดยหลักการคือมันจะให้คนที่เรียกใช้เป็นคนกำหนดเองว่า data type ที่เขาจะทำงานด้วยคืออะไรนั่นเอง

### 🔥 ใช้กับตัวแปร

จากโค้ดตัวอย่างด้านบน เราก็สามารถเอา Generic เติมเข้าไปได้เลยเป็นแบบนี้

```csharp
public class SimpleStorage<T>
{
    public T Value;
}
```

จะเห็นว่าบรรทัดที่ 1 ถูกเพิ่ม \<T> ต่อท้ายเข้าไป ซึ่งเจ้านี่แหละคือ generic และส่วนบรรดทัดที่ 2 แทนที่เราจะกำหนดให้เป็น int เราก็เอาเจ้า T จากบรรทัดที่ 1 มาใส่ไว้นั่นเอง **อ่านแล้ว งงๆ อยู่ไม่เป็นไร ไปดูต่อกัน**

คราวนี้เวลาที่เราจะเรียกใช้คลาสนี้ เราก็จะต้องสร้าง object ของมันพร้อมกับบอกว่าเจ้าคลาสนี้จะทำงานกับ data type อะไรนั่นเอง เช่น อยากทำงานกับ int เราก็จะเขียนโค้ดออกมาเป็นแบบนี้

```csharp
static void Main(string[] args)
{
    SimpleStorage<int> s = new SimpleStorage<int>();
    s.Value = 55;
    Console.WriteLine(s.Value);
}
```

จากโค้ดด้านบนจะเห็นว่าตอนที่สร้าง object ของคลาส **SimpleStorage** เราจะต้องบอกว่า เจ้าคลาสนี้จงทำงานกับ **int** ซะ (ในบรรทัดที่ 3) ดังนั้นตัวแปรที่ชื่อว่า **Value** เลยมี data type เป็น int นั่นเอง เลยทำให้มันเก็บค่าตัวเลขลงไปได้ (ในบรรทัดที่ 4)

จากที่ว่ามาเราก็อาจจะสร้าง object ของคลาส SimpleStorage เพื่อให้มันทำงานกับ data type ที่เราอยากทำงานด้วยหลายๆแบบก็ได้ ตามโค้ดนี้เลย

```csharp
var s1 = new SimpleStorage<int>();
s1.Value = 55;

var s2 = new SimpleStorage<string>();
s2.Value = "Hello world";

var s3 = new SimpleStorage<bool>();
s3.Value = false;
```

### 🔥 ใช้กับ Method

นอกจากที่เราจะใช้กับตัวแปรได้แล้ว เรายังสามารถเอา Generic มาใช้กับ method ได้ด้วยนะ ซึ่งถ้าเราใช้กับ method เราสามารถกำหนด generic ไว้กับ method ได้เลย ประมาณนี้

```csharp
public class Awesome
{
    public void ShowIt<T>(T value)
    {
        Console.WriteLine(value);
    }
}
```

ซึ่งตอนเรียกใช้ก็จะราวๆนี้

```csharp
int a = 55;
var awe = new Awesome();
awe.ShowIt(a);
```

และรวมถึงเราจะเอาไปให้มันเป็น return type ก็ได้นะ

```csharp
public T ShowIt<T>(T value)
{
    Console.WriteLine(value);
    return value;
}
```

### 🔥 Generic แบบหลายตัว

ในบางทีเราก็อยากให้มันทำ generic ไว้หลายๆตัวเราก็สามารถทำได้นะ โดยการคั่น generic แต่ละตัวด้วยคอมม่ายังไงล่ะ

```csharp
public class Awesome<T,U>
{
    public T Value1 { get; set; }
    public U Value2 { get; set; }
}
```

คนเรียกใช้งานก็จะต้องเรียกราวๆนี้

```csharp
var awe = new Awesome<int, string>();
awe.Value1 = 55;
awe.Value2 = "Hello world";
```

## 🤔 ขอตัวอย่างที่เอาไปใช้งานจริงๆหน่อย

มีตรึมเลยที่ Microsoft เขาเขียนไว้เบื้องต้นให้เราแล้ว เช่นเราอยากเก็บข้อมูลในรูปแบบ Stack เราก็สามารถใช้คลาส **Stack\<T>** ได้เลย

```csharp
var stack = new Stack<int>();
stack.Push(5);
stack.Push(9);
var lastedValue = stack.Pop();
Console.WriteLine(lastedValue);  // 9
```

## 🎯 บทสรุป

ความสามารถของ generic จะช่วยให้ คนที่เรียกใช้งานเป็นคนกำหนดเองได้ว่า เขาอยากทำงานกับ data type อะไร ซึ่งจะช่วยทำให้โค้ดของเรามีความยืดหยุ่นมากขึ้น และนำกลับมาใช้ใหม่ได้มากขึ้นนั่นเอง

{% hint style="success" %}
**แนะนำให้อ่าน**\
ในการใช้งาน generic นั้นจริงๆมันทำได้เยอะม๊วกกกกก เช่นใช้กับ interface , method, delegate บลาๆ แนะนำให้ไปอ่านเอาต่อนะเพราะไม่งั้นบทความนี้จะยาวเป็นหางว่าวเลย\
[Microsoft document - Generics](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/)
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.saladpuk.com/beginner-1/csharp101/advanced/generic.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
