{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "display_name": "C#",
   "language": "csharp",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "csharp"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "id": "gen00",
   "metadata": {},
   "source": "# Generics\n\nYou have been using generics since Chapter 7: `List<T>`, `Dictionary<TKey, TValue>`, `Stack<T>`. Here we look at what `<T>` actually means and how to write your own generic types and methods.",
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "gen01",
   "metadata": {},
   "source": "## Why Generics Exist\n\nWithout generics, a `List` that holds `int` would be a different class from one that holds `string`. Generics let one implementation work for *any* type:\n\n```csharp\n// Non-generic \u2014 stores object, loses type safety\nvar list = new ArrayList();\nlist.Add(1);\nlist.Add(\"oops\");  // compiles \u2014 bug at runtime\n\n// Generic \u2014 type-safe at compile time\nvar nums = new List<int>();\nnums.Add(1);\n// nums.Add(\"oops\");  // compile error\n```",
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "gen02",
   "metadata": {},
   "source": "## Generic Methods",
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "gen03",
   "metadata": {},
   "source": "// A generic method works for any type T\nstatic T[] Repeat<T>(T value, int count)\n{\n    var arr = new T[count];\n    for (int i = 0; i < count; i++) arr[i] = value;\n    return arr;\n}\n\nint[]    ints    = Repeat(0, 5);       // [0, 0, 0, 0, 0]\nstring[] strings = Repeat(\"hi\", 3);   // [\"hi\", \"hi\", \"hi\"]\n\nConsole.WriteLine(string.Join(\", \", ints));     // 0, 0, 0, 0, 0\nConsole.WriteLine(string.Join(\", \", strings));  // hi, hi, hi",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "id": "gen04",
   "metadata": {},
   "source": "## Generic Classes",
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "gen05",
   "metadata": {},
   "source": "// A minimal generic pair\nclass Pair<T1, T2>\n{\n    public T1 First  { get; }\n    public T2 Second { get; }\n\n    public Pair(T1 first, T2 second) { First = first; Second = second; }\n\n    public override string ToString() => $\"({First}, {Second})\";\n}\n\nvar p1 = new Pair<string, int>(\"Alice\", 30);\nvar p2 = new Pair<double, bool>(3.14, true);\n\nConsole.WriteLine(p1);  // (Alice, 30)\nConsole.WriteLine(p2);  // (3.14, True)",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "id": "gen06",
   "metadata": {},
   "source": "## Type Constraints\n\nUse `where T : ...` to restrict what types `T` can be:\n\n| Constraint | Meaning |\n|---|---|\n| `where T : class` | T must be a reference type |\n| `where T : struct` | T must be a value type |\n| `where T : new()` | T must have a parameterless constructor |\n| `where T : ISomeInterface` | T must implement the interface |\n| `where T : BaseClass` | T must inherit from `BaseClass` |",
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "gen07",
   "metadata": {},
   "source": "// Constraint: T must implement IComparable<T> so we can compare\nstatic T Max<T>(T a, T b) where T : IComparable<T>\n    => a.CompareTo(b) >= 0 ? a : b;\n\nConsole.WriteLine(Max(3, 7));          // 7\nConsole.WriteLine(Max(\"apple\", \"banana\")); // banana",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "id": "gen08",
   "metadata": {},
   "source": "## Generic Interfaces and LINQ\n\nAll LINQ extension methods are generic \u2014 `Where<T>`, `Select<T, TResult>`, etc. You have been calling them without writing the type arguments because C# *infers* them:\n\n```csharp\n// These two are identical:\nnums.Where<int>(x => x > 0);\nnums.Where(x => x > 0);  // T inferred as int\n```\n\nType inference makes generics feel like ordinary method calls at the use site.",
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "gen09",
   "metadata": {},
   "source": "## Practice\n\n1. Write a generic `static T First<T>(List<T> list)` that returns the first element or throws `InvalidOperationException` if the list is empty.\n2. Write a generic `static void Swap<T>(ref T a, ref T b)` and test it with `int` and `string`.\n3. Write a generic `Stack<T>` class backed by a `List<T>` with `Push`, `Pop`, and `Peek` methods. Add a constraint so `T` must be non-nullable.",
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "gen10",
   "metadata": {},
   "source": "```{rubric} Footnotes\n```",
   "outputs": []
  }
 ]
}