# Generic classes

> A class can declare type parameters, so one shape serves every type it is used with instead of being copied per entity. The type argument is written where the class is used (`Column<Report>`), and every member read off it substitutes — which is what binds a selector's parameter to the real row type and makes its body checkable. A METHOD can declare its own type parameters too, and those are bound by the call rather than by the receiver.

<!-- id: class-generics · area: class · stability: preview · html: https://osysharp.com/reference/class/generics/ -->

## Summary        {#summary}
A class that describes **how to work with a value** should not have to know **which** value. Declare it with a type
parameter and one declaration serves every type:

```osy title="one column shape, any row type" test app=class-generics
class Column<T> {
  public string Label;
  public Func<T, string> Value;
}
```

Without generics that shape has to be copied once per entity — a `ReportColumn`, a `PersonColumn` — each identical
except for one type. With them, `Column<Report>` and `Column<Person>` are two **types** from one **declaration**.

## Signature      {#signature}
```osy title="declaring the parameter, supplying the argument" syntax
class Name<T> { … }            // one type parameter
class Name<TIn, TOut> { … }    // several, comma-separated

Name<Report>                   // a CONSTRUCTED type: the argument supplied at the use site
new Name<Report> { … }         // …and at construction
```

A type parameter is in scope over the whole class body — including in its methods' signatures. A **method** may also
declare parameters of its own, which the call binds rather than the receiver:

```osy title="a method's own parameter, bound by the call" syntax
class Util { public T Echo<T>(T v) { … } }    // the method's own parameter
T Echo<T>(T v) { … }                          // a top-level function, the same way
```

`entity` cannot be generic.

## Description    {#description}

### The type argument goes where the class is USED   {#use-site}
The declaration names the parameter; the use site supplies the argument. Between them, the compiler knows what `T`
is at every point:

```osy title="the argument is written at the use site" test app=class-generics
entity Report { [MaxLength(80)] string Title; decimal Total; }

[Composable] component Grid<T>(T[] rows, Column<T>[] columns) {
  render {
    foreach (var r in rows) {
      Row { foreach (var c in columns) { Text(c.Value(r)); } }
    }
  }
}

[Page("/")] [AllowAnonymous]
component Home() {
  var reports = Report.ToList();          // the read is a MEMBER — a render body holds values, it does not fetch
  render {
    Grid(rows: reports, columns: [
      new Column<Report> { Label = "Title", Value = r => r.Title },
      new Column<Report> { Label = "Band",  Value = r => r.Total > 1000 ? "large" : "small" }
    ]);
  }
}
```

`Grid` mentions no entity at all. `Column<T>` mentions no entity. The only place `Report` appears is the call site —
which is exactly the point.

### Reading a member substitutes the argument   {#substitution}
`Value` is declared `Func<T, string>`. Read off a `Column<Report>` it **is** a `Func<Report, string>`, so the lambda's
parameter binds to a real row and its body is checked against it:

```osy title="the selector's parameter is the row type — and typos are caught" syntax
new Column<Report> { Value = r => r.Title }    // ✓ r is a Report
new Column<Report> { Value = r => r.Nmae }     // ✗ 'Report' has no property 'Nmae'
```

That substitution is the whole feature. Passing the wrong one is caught too — a `Column<Person>` where a
`Column<Report>` is expected is a different type, not merely a differently-labelled `Column`.

### Where a type parameter may appear   {#positions}
`T` is first-class **on its own**, as an **array**, and inside a **function type**:

```osy title="the shapes a member may take" test app=class-generics
class Holder<T> {
  public T Row;                      // the type parameter itself
  public T[] Rows;                   // an array of it
  public Func<T, string> Read;       // a selector over it
}
```

What is not built yet is `T` inside a **mutable collection or another generic type** — `List<T>`, `HashSet<T>`,
`Dictionary<string, T>`. Those carry a collection kind and a value slot that the compiler derives from the
declaration alone, so they need their own storage rather than being let through. The refusal names the three
spellings that do work.

An unset member typed `T` reads `default(T)`, decided by the type argument: a `Holder<decimal>` reads `0m`, a
`Holder<Report>` reads null — because null *is* `default` for a type with no meaningful zero.

### Methods can have their OWN type parameters   {#method-generics}
A method may declare type parameters that the class does not, and they are bound by the **call** rather than by the
receiver — inferred from the argument types:

```osy title="one method, a different type at every call" test app=class-generic-methods
class Util {
  public T Echo<T>(T v) { return v; }
  public string Describe<T>(T v) { return "described"; }
}

string UseIt() {
  var u = new Util();
  var n = u.Echo(2m);                // T is decimal here
  var s = u.Echo("hello");           // …and string here, from the same declaration
  return s;
}
```

This is the difference worth holding on to: a generic **class** fixes its argument once, where it is used
(`Column<Report>`), and every member read off it substitutes that one answer. A generic **method** decides per call.

Write the arguments explicitly when inference has nothing to read them from — a type parameter that appears in no
parameter cannot be inferred, and the compiler says so rather than guessing:

```osy title="explicit type arguments" test app=class-generic-methods
string Explicitly() {
  var u = new Util();
  return u.Echo<string>("hello");
}
```

A **top-level function** may be generic the same way, and so may a method on a class that is itself generic — there
the two sets are bound by different things, the class's by the receiver and the method's by the call. A method may
**not** reuse one of its class's parameter names: the two would be different types wearing one name, and nothing in
the source would show the reader which is which.

### Classes only   {#no-generic-entity}
An `entity` cannot be generic. An entity is a table, and a table has no columns until `T` is known:

```osy title="a table cannot be generic" syntax
entity Row<T> { string Label; }      // ✗ — write `class Row<T>` for an in-memory shape
```

## Examples       {#examples}

A comparator and a formatter are the same shape as a column, which is why this generalises past grids:

```osy title="the same idea, two other uses" test app=class-generics-more
class Sorter<T> {
  public string Label;
  public Func<T, string> Key;
}

class Formatter<T> {
  public Func<T, string> Render;
}
```

Several type parameters are written the way C# writes them:

```osy title="more than one parameter" test app=class-generics-more
class Mapping<TIn, TOut> {
  public Func<TIn, string> Read;
  public Func<TOut, string> Write;
}
```

### Deriving from a generic {#deriving}

A base or a contract may be **constructed** — `: Box<int>`, `: IRepo<string>`. The members that arrive are
substituted, so the subtype holds the closed type and not the parameter:

```osy title="closing a generic base" test app=class-generics-derive
class Box<T> {
  public T Value;
  public T Get() { return Value; }
}

// `Value` is an `int` on IntBox, so ordinary arithmetic works on it.
class IntBox : Box<int> {
  public int Doubled() { return Value * 2; }
}
```

The subtype may stay generic and pass its own parameter on, and a third type close it. Substitution **composes**
through the chain, so `Leaf.Value` is a `string`:

```osy title="passing a parameter on" test app=class-generics-derive
class Mid<U> : Box<U> { public string Tag = "mid"; }
class Leaf : Mid<string> { }
```

A contract works the same way, and an implementor satisfies it **at the arguments it named** — a `TextRepo` is an
`IRepo<string>` and is not an `IRepo<int>`:

```osy title="a generic contract" test app=class-generics-derive
interface IRepo<T> { T Fetch(); void Store(T v); }

class TextRepo : IRepo<string> {
  public string Held = "";
  public string Fetch() { return Held; }
  public void Store(string v) { Held = v; }
}
```

A `where` on a subtype goes after the base list, exactly as in C#:

```osy title="passing a constraint on" test app=class-generics-derive
class Shape { public string Kind = "shape"; }
class Holder<T> where T : Shape { public T Held; }
class ShapeHolder<T> : Holder<T> where T : Shape { }
```

## Errors         {#errors}

| What you wrote | What you get |
|---|---|
| `entity Row<T> { … }` | *'Row' is an `entity`, and an entity cannot be generic — it is a table, and a table has no columns until its type argument is known.* |
| `public List<T> Rows;` on a `class Holder<T>` | *'Holder.Rows' is typed `List<T>`, and a type parameter is not supported inside a mutable collection or a generic type yet — it works on its own (`T Rows;`), as an ARRAY (`T[] Rows;`), or inside a function type (`Func<T, string> Rows;`).* |
| `Column<Report, Report>` | *'Column' takes 1 type argument (T), but 2 were written.* |
| `u.Echo<decimal, string>(2m)` on `T Echo<T>(T v)` | *'Echo' declares 1 type parameter ('T'), but 2 were given.* |
| calling `string Make<T>()`, which mentions `T` in no parameter | *'Make' cannot infer its type parameter 'T' from these arguments … Write the type argument explicitly: `Make<T>(…)`.* |
| `public string Show<T>(T v)` inside a `class Box<T>` | *'Show' declares a type parameter 'T', and so does the type that declares it … Rename one of them.* |
| `c.Add<decimal>(1m)` where `Add` declares no type parameters | *'Add' declares no type parameters, so the type argument in `Add<…>(…)` binds to nothing … Drop the type arguments.* |
| `Column` with no argument | *'Column' is generic — it needs a type argument for 'T'.* |
| a `Column<Person>` where a `Column<Report>` is expected | *expects `Column<T>[]` for 'columns', but got `Column<Person>[]`.* |
| `class Bad : Box<int, string>` on a `class Box<T>` | *'Box' takes 1 type argument (T), but 2 were written on `Bad`.* |
| `class Bad2 : NotGeneric<int>` | *'NotGeneric' is not generic, so `Bad2 : NotGeneric<int>` has nowhere to put those arguments.* |
| `class BadBox : Box<int>` on a `class Box<T> where T : Shape` | *'Box' constrains 'T' to 'Shape', and 'int' is not a class at all — a bound names a type to derive from.* |
| `IRepo<string> r = new IntRepo();` where `IntRepo : IRepo<int>` | *cannot implicitly convert 'IntRepo' to 'IRepo<string>'.* |

## See also       {#see-also}
- [Func<T, R>](https://osysharp.com/reference/ui/function-value/) — `Func<T, R>` as a value: what a selector IS, and why its body reads only its parameters
- [Classes](https://osysharp.com/reference/class/index/) — the class itself: a value shape, never a table
- [class properties](https://osysharp.com/reference/class/properties/) — members that run a body on access
- [component](https://osysharp.com/reference/ui/component/) — generic components, which take their type argument by inference from the call site
