# Class inheritance

> A class can derive from another class with `class Circle : Shape`, inheriting its fields and its methods to any depth. A value of the derived type goes wherever the base is expected, and a `virtual` method can be replaced by an `override` one. `sealed` closes a class to further derivation. A class may derive only from a class — never from an entity, and never the reverse.

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

## Summary        {#summary}
A class derives from another with `:`, exactly as in C#. It inherits the base's fields and methods:

```osy title="a derived class inherits fields and methods" test app=class-inheritance
class Shape {
  public string Name;

  public string Describe() {
    return Name;
  }
}

class Circle : Shape {
  public decimal Radius;
}

string DescribeACircle() {
  var c = new Circle { Name = "small", Radius = 2m };
  return c.Describe();          // the method Shape declares, called on a Circle
}
```

## Signature      {#signature}
```osy syntax
class Derived : Base { … }      // inherits Base's fields and methods
sealed class Leaf { … }         // no type may derive from Leaf
```

## Description    {#description}

### What does a subclass inherit?   {#what-is-inherited}
Fields and methods, to any depth. A three-level chain works the way it reads, and a member declared anywhere above is
available below:

```osy title="inheritance is transitive" test app=class-inheritance-depth
class Shape {
  public string Name;
}

class Round : Shape {
  public decimal Radius;
}

class Dot : Round { }

string NameOfADot() {
  var d = new Dot { Name = "tiny", Radius = 0m };
  return d.Name;                // declared two levels up
}
```

### A derived value fits a base slot   {#upcast}
This is the point of a hierarchy: code written against `Shape` accepts every shape. The conversion is implicit and
needs nothing written:

```osy title="a Circle goes wherever a Shape is expected" test app=class-inheritance-upcast
class Shape { public string Name; }
class Circle : Shape { public decimal Radius; }

string NameThrough() {
  Shape s = new Circle { Name = "c", Radius = 2m };
  return s.Name;
}
```

It is **one-way**, as in C#. A `Shape` is not assignable to a `Circle`, because not every shape is one — going the
other way needs a [type test](https://osysharp.com/reference/class/type-tests/).

The same rule applies wherever two values meet and one type has to describe both — a conditional, a `switch`
expression, a `??` fallback. The answer is the **base** of the two:

```osy title="picking between a base and a derived value" test app=class-inheritance-upcast-positions
class Shape { public string Name; }
class Circle : Shape { public decimal Radius; }

string Pick(bool round, int kind) {
  var c = new Circle { Name = "circle", Radius = 2m };
  var p = new Shape { Name = "plain" };

  Shape a = round ? c : p;              // the branches unify to Shape
  Shape b = kind switch { 1 => c, _ => p };   // so do the arms
  Shape d = a ?? c;                     // and so do the sides of `??`
  return a.Name + b.Name + d.Name;
}
```

Because the result is a `Shape`, only `Shape`'s members are readable through it — reach for a
[type test](https://osysharp.com/reference/class/type-tests/) to get back to `Radius`.

**Two SIBLINGS take the type they are written into.** A `Circle` and a `Square` are both `Shape`s, but neither is the
other, so there is no type to *infer* — and where the type is *written*, that is the answer:

```osy title="the target types the conditional" test app=class-inheritance-target-typed
class Shape { public string Name; }
class Circle : Shape { public decimal Radius; }
class Square : Shape { public decimal Side; }
class Plot { public Shape Primary; }

string Show(Shape s) { return s.Name; }

string Pick(bool round) {
  var c = new Circle { Name = "circle", Radius = 1m };
  var q = new Square { Name = "square", Side = 2m };

  Shape s = round ? c : q;                        // a declared local
  var p = new Plot { Primary = round ? c : q };   // a member
  return Show(round ? c : q) + s.Name + p.Primary.Name;   // an argument
}
```

It works wherever the target is written: a declared local, a parameter, a member, an assignment, a `return`, and the
arms of a `switch` expression. What it does **not** do is invent one — `var s = round ? c : q;` writes the value
into nothing, so there is nothing to take, and the compiler says so and names the fix. (Same in C#.)

### Can a `Circle[]` be used as a `Shape[]`?   {#covariance}
A `Circle[]` **is** a `Shape[]`, and needs nothing written — as in C#. This holds in every position: a local, a
parameter, a return, a class member.

```osy title="Circle[] flows into Shape[]" test app=class-inheritance-array-covariance
class Shape { public string Name; }
class Circle : Shape { public decimal Radius; }

int CountShapes(Shape[] shapes) { return shapes.Count(); }

int HowMany() {
  Circle[] cs = [new Circle { Name = "a", Radius = 1m }, new Circle { Name = "b", Radius = 2m }];
  Shape[] xs = cs;
  return CountShapes(xs);
}
```

A **`List<T>` is different, and deliberately so**: it can be appended to, so a `List<Circle>` is *not* a
`List<Shape>`. Handing one over would let the receiver add a plain `Shape` to your list of circles. If the receiver
only reads, declare it `Shape[]` — a `List` passes straight into a read-only sequence.

**An array of a type that HAS subtypes cannot be written through.** This is the other half of the rule above, and
the reason the conversion is safe: since a `Shape[]` may really be a `Circle[]`, storing a plain `Shape` into one
would leave an element missing the members the narrower array promises.

```osy title="✗ writing into an array that may be a Circle[]" syntax
Shape[] xs = circles;              // fine — read it all you like
xs[0] = new Shape { Name = "x" };  // refused: `Shape` has subtypes, so this array may be a `Circle[]`
```

C# allows that write and throws `ArrayStoreException` when it runs; here it is the same rule, moved to where you can
see it. An array of a type nothing derives from — including every scalar array, `int[]`, `double[]`, `string[]` — is
written exactly as in C#. When you need to write into a polymorphic sequence, use a `List<Shape>`: it is invariant,
which is what makes it safe to write.

### `sealed` closes the class   {#sealed}
`sealed` says no type may derive. Write it when a class is meant to be the end of its line:

```osy syntax
sealed class Candle : Mark { }  // deriving from Candle is a compile error
```

### Specialising a method: `virtual` and `override`   {#override}
A base method marked `virtual` may be replaced by a derived one marked `override`. The call runs the method of the
value's **actual** type, whatever type the slot holding it is declared as:

```osy title="the derived body runs through a base-typed slot" test app=class-inheritance-override
class Shape {
  public string Name;
  public virtual decimal Area() { return 0m; }
}

class Circle : Shape {
  public decimal Radius;
  public override decimal Area() { return Radius * Radius * 3m; }
}

decimal AreaThroughTheBase() {
  Shape s = new Circle { Name = "c", Radius = 2m };
  return s.Area();              // 12 — Circle's body, not Shape's
}
```

**Both words are required, each for a different mistake.** Without `virtual` on the base, adding a method to a base
class would silently change what every subclass sharing that name does. Without `override` on the derived one, an
accidental name collision would read as a deliberate specialisation. Redeclaring a method that is not `virtual` is
refused, and the refusal names the word that is missing.

**`base.M()` calls the version the derived class inherits** — what makes an override able to EXTEND the base rather
than replace it:

```osy title="an override that builds on its base" test app=class-inheritance-base-call
class Shape {
  public string Name;
  public virtual decimal Area() { return 2m; }
}

class Circle : Shape {
  public decimal Radius;
  public override decimal Area() { return base.Area() + 1m; }   // 3 — Shape's answer, plus one
}

decimal AreaOfACircle() {
  Shape s = new Circle { Name = "c", Radius = 1m };
  return s.Area();
}
```

`base` looks up the chain, not just one step: if the immediate parent declares nothing by that name, the call runs
the nearest ancestor that does. It is only meaningful inside a class member's body, and a local variable named
`base` shadows it.

### Constructing the base: `: base(…)`   {#base-constructor}
A derived class's constructor runs the base class's constructor first, and says which one with `: base(…)`:

```osy title="the base constructor runs first" test app=class-inheritance-base-ctor
class Shape {
  public string Name;
  public decimal Width;
  public Shape(string n, decimal w) { Name = n; Width = w; }
}

class Circle : Shape {
  public decimal Radius;
  public Circle(string n, decimal r) : base(n, r * 2m) { Radius = r; }
}

string BuildOne() {
  var c = new Circle("small", 2m);
  return c.Name;                 // "small" — set by Shape's constructor
}
```

The arguments are an ordinary argument list: as many as the base constructor takes, in any expression, and by name
(`: base(n, w: 4m)`) if you prefer. `: base()` is how you call a parameterless base constructor explicitly.

A constructor with **no** initializer runs the base's *parameterless* constructor, exactly as in C#. So if the base
declares a constructor that takes arguments, the derived one has to say what to pass — and the compiler asks for it
by name. A base class with no constructor at all needs nothing: its fields take the defaults their declarations give.

Members the base constructor assigns count as assigned, so you do not have to supply them again at the create site.

`: this(…)` — chaining to another constructor of the same class — needs constructor overloading, which is not
available yet.

### `abstract` — a shape to derive from   {#abstract}
An `abstract class` cannot be created; it exists for other types to fill in. An `abstract` method declares **what** a
subclass must provide and has no body:

```osy title="the obligation, and the class that meets it" test app=class-inheritance-abstract
abstract class Shape {
  public string Name;
  public abstract decimal Area();        // no body — every Shape has one, but Shape does not say what
}

class Circle : Shape {
  public decimal Radius;
  public override decimal Area() { return Radius * Radius * 3m; }
}

decimal AreaOfACircle() {
  Shape s = new Circle { Name = "c", Radius = 2m };
  return s.Area();                       // 12 — Circle's body
}
```

An abstract method is already the thing a subclass overrides, so it needs no `virtual`. The first **concrete** class
below it must provide a body — a class that does not is asked for one by name, unless it is `abstract` too, in which
case the obligation passes down. `abstract` and `sealed` are opposites and cannot both be written: one says the type
must be derived from, the other that it must not.

### `protected` — visible down the chain, and nowhere else   {#protected}
A class member is `private` unless it says otherwise, and `private` means *this class only* — a subclass cannot see
it. `protected` is the middle setting: reachable from the declaring class and from anything that derives from it.

```osy title="a subclass reads it; nothing else can" test app=class-inheritance-protected
class Shape {
  protected string Tag;                      // subclasses may read and write it
  private string secret;                     // this class only, even for a subclass

  public Shape(string t) { Tag = t; secret = "hidden"; }
  protected string Describe() { return "[" + Tag + "]"; }   // methods take it too
}

class Circle : Shape {
  public decimal Radius;
  public Circle(string t, decimal r) : base(t) { Radius = r; }
  public string Label() { return Tag + Describe(); }        // both reachable here
}

string BuildLabel() {
  var c = new Circle("c", 2m);
  return c.Label();                          // "c[c]"
}
```

It reaches the whole chain, not one step: a class deriving from `Circle` sees `Shape`'s protected members too.

From outside the hierarchy the member does not exist — `c.Tag` in a top-level function is a compile error naming the
fix, which is to derive from the class rather than to reach into it.

### Can a class derive from an entity?   {#kinds}
A class may derive only from a class. Mixing the kinds is refused in both directions, because they are different
things wearing one word: an [entity hierarchy](https://osysharp.com/reference/entity/inheritance/) is rows in a table with a discriminator column,
and a class is a value in memory with no table at all.

## Errors         {#errors}
| you wrote | what you get |
|---|---|
| a method already declared on the base | refused, naming `virtual`/`override` as what is missing |
| `class C : SomeSealedClass` | refused — `sealed` means no type may derive |
| `class C : SomeEntity` (or an entity deriving from a class) | refused — a class and an entity are different kinds |
| `Circle c = someShape;` | refused — the upcast is one-way; test the type instead |
| `var s = f ? circle : square;` | refused — two siblings have no common type and `var` supplies no target; declare the type, or cast one branch |
| a `List<Circle>` where a `List<Shape>` is wanted | refused — a `List` can be appended to, so it is invariant; declare `Shape[]` if it is only read |
| `shapes[0] = new Shape { … }` where `Shape` has subtypes | refused — the array may be a `Circle[]`; read it freely, or use a `List<Shape>` to write |

## See also       {#see-also}
- [Interfaces — a contract several types can satisfy](https://osysharp.com/reference/class/interfaces/) — a contract with no bodies and no state, which a type may satisfy SEVERAL of
  (a base class it may have only one of)
- [Testing which class a value is](https://osysharp.com/reference/class/type-tests/) — asking which type a value actually is, and narrowing to it
- [class methods](https://osysharp.com/reference/class/methods/) — behaviour with a receiver
- [entity Sub : Base](https://osysharp.com/reference/entity/inheritance/) — the same word for rows, and why it works differently
- [Classes](https://osysharp.com/reference/class/index/) — what a class is
