EMZETT.
Login

Kurz: class Tier { public string Name { get; } public Tier(string name) => Name = name; public virtual string Laut() => ”…”; // virtual: darf überschrieben werden public string Vorstellen() => $“{Name} macht {Laut()}”; }

Teil des Kurses C#

Kapitel 8 von 16 im Kurs C#. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

List<Tier> tiere = [new Hund("Rex"), new Katze("Mimi"), new Tier("Etwas")];
foreach (var t in tiere) Console.WriteLine(t.Vorstellen());          // Polymorphie
 
class Tier
{
    public string Name { get; }
    public Tier(string name) => Name = name;
    public virtual string Laut() => "...";                          // virtual: darf überschrieben werden
    public string Vorstellen() => $"{Name} macht {Laut()}";
}
 
class Hund : Tier
{
    public Hund(string name) : base(name) { }                       // Konstruktor der Basis
    public override string Laut() => "Wuff";
}
 
class Katze : Tier
{
    public Katze(string name) : base(name) { }
    public override string Laut() => "Miau";
}

Ausgabe:

Rex macht Wuff
Mimi macht Miau
Etwas macht ...
  • class B : A erbt von A; eine Basisklasse, aber beliebig viele Interfaces
  • virtual erlaubt Überschreiben, override überschreibt, base ruft die Basis auf
  • sealed verbietet weitere Vererbung/Überschreiben
  • Alle Klassen erben von object (ToString, Equals, GetHashCode)

Abstrakte Klassen

Form[] formen = [new Kreis(1), new Quadrat(3)];
foreach (var f in formen) Console.WriteLine(f.Info());
 
abstract class Form
{
    public abstract double Flaeche();                               // muss überschrieben werden
    public string Info() => $"{GetType().Name} mit Fläche {Flaeche():F2}";
}
class Kreis(double r) : Form { public override double Flaeche() => Math.PI * r * r; }
class Quadrat(double a) : Form { public override double Flaeche() => a * a; }

Ausgabe:

Kreis mit Fläche 3.14
Quadrat mit Fläche 9.00

Interfaces

Ein Interface beschreibt einen Vertrag. Eine Klasse kann mehrere umsetzen:

var e = new ElektroAuto();
e.Laden(50);
IFahrbar f = e;
f.Fahren();
Console.WriteLine(f.Art());
IFahrbar rad = new Fahrrad();
rad.Fahren();
Console.WriteLine(rad.Art());
 
interface IFahrbar
{
    void Fahren();
    string Art() => "Fahrzeug";                                     // Standardimplementierung
}
interface IAufladbar { void Laden(int prozent); }
 
class ElektroAuto : IFahrbar, IAufladbar
{
    private int _akku = 20;
    public void Fahren() => Console.WriteLine($"surr, Akku {_akku}%");
    public void Laden(int prozent) => _akku = Math.Min(100, _akku + prozent);
    public string Art() => "Elektroauto";
}
class Fahrrad : IFahrbar { public void Fahren() => Console.WriteLine("tret tret"); }

Ausgabe:

surr, Akku 70%
Elektroauto
tret tret
Fahrzeug

Namenskonvention: Interfaces beginnen mit I (IList, IDisposable, IComparable).

Wichtige eingebaute Interfaces

InterfaceBedeutung
IDisposableRessourcen freigeben (using)
IEnumerable<T>kann mit foreach durchlaufen werden
IComparable<T>hat eine natürliche Reihenfolge (Sort)
IEquatable<T>typsichere Gleichheit
IComparer<T>Vergleichsregel von außen
ICloneablekopierbar
var l = new List<Version> { new(1, 10), new(1, 2), new(2, 0) };
l.Sort();
Console.WriteLine(string.Join(" < ", l));
 
class Version : IComparable<Version>
{
    public int Major { get; }
    public int Minor { get; }
    public Version(int major, int minor) { Major = major; Minor = minor; }
    public int CompareTo(Version? other) => other is null ? 1 : (Major, Minor).CompareTo((other.Major, other.Minor));
    public override string ToString() => $"{Major}.{Minor}";
}

Ausgabe:

1.2 < 1.10 < 2.0

Komposition und Abhängigkeiten über Interfaces

Programmiere gegen Schnittstellen statt konkrete Klassen. So lassen sich Teile austauschen und testen:

var dienst = new BestellDienst(new KonsolenMailer());
dienst.Bestelle("Mia");
 
interface IMailer { void Sende(string an, string text); }
class KonsolenMailer : IMailer { public void Sende(string an, string text) => Console.WriteLine($"Mail an {an}: {text}"); }
 
class BestellDienst(IMailer mailer)                                 // Abhängigkeit hereingegeben
{
    public void Bestelle(string kunde) => mailer.Sende(kunde, "Danke für deine Bestellung");
}

Ausgabe:

Mail an Mia: Danke für deine Bestellung

Dieses Muster heißt Dependency Injection. ASP.NET Core bringt dafür einen eingebauten Container mit.

Merke

  • virtual/override/base/sealed steuern Vererbung; eine Basisklasse, viele Interfaces
  • abstract für Vorlagen mit Pflicht-Methoden
  • Interfaces (IName) beschreiben Verträge, auch mit Standardimplementierungen
  • IComparable<T>, IEnumerable<T>, IDisposable sind die wichtigsten Standard-Interfaces
  • Abhängigkeiten über Interfaces hereingeben (Dependency Injection)

Übungsaufgabe

Entwirf ein Interface IZahlung mit Betrag() und zwei Klassen. Summiere die Beträge einer gemischten Liste.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Klassen, Eigenschaften und Konstruktoren

Weiter: Records, Structs, Enums und Pattern Matching

Alle Kapitel: C# im Überblick