Polymorphie Überladen / Überschreiben • Definition • Methodennamen überladen • Methoden überschreiben • Konstruktoren überladen • Beispiele Dr. Beatrice Amrhein Definition 2 Definition: Überladen (von Namen) Überladen bedeutet, dass ein Methodenname in einer Klasse für mehrere Methoden verwendet wird. Damit das Überladen möglich ist, muss wenigstens eine der beiden Voraussetzungen erfüllt sein: o Der Datentyp mindestens eines Übergabeparameters ist anders als in den anderen gleichnamigen Methoden. o Die Anzahl der Übergabeparameter ist unterschiedlich. Beispiel, überladen des Namens add int add( int a, int b ){ return a+b; } float add( int a, int b, float c ){ return a+b+c; } 3 Definition: Überschreiben (von Methoden) Eine Methode kann in einer davon abgeleiteten Klasse überschrieben werden. Beim Überschreiben einer Methode müssen die beiden Methodensignaturen (Methodenname, Parameterliste, Rückgabewert) exakt(!) übereinstimmen. class BasisKlasse { public virtual int MachEtwas(int a) { return a + a; } } class AbgeleiteteKlasse:BasisKlasse { public override int MachEtwas(int a) { return a * a; } } 4 Methodennamen überladen 5 Regeln für das Überladen Methoden, welche den gleichen Namen, aber eine unterschiedliche Parameterliste haben, heissen überladene Methoden. Die zwei Methoden dürfen sich aber nicht nur durch den Rückgabewert oder den Zugriffs-Modifizierer (public, private, …) unterscheiden. 6 Beispiel Klasse Addierer Die Klasse Addierer habe für verschiedene Typen verschiedene addMethoden class Addierer { public int add(int a, int b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } // drei Parameter, ok public float add(float a, float b) { return a + b; } // andere Typen, ok //Unterscheidung nur durch Rückgabewert und Modifizierer private void add(int a, int b) // Fehler!! { Console.WriteLine(a + b); } } 7 Stil-Regeln für das Überladen Überladene Methoden müssen Varianten der gleichen Funktionalität bereitstellen (alle Methoden machen etwas ähnliches) Es ist guter Stil, wenn die Namen und die Reihenfolge der gemeinsamen Parameter möglichst gleich bleiben. Es ist erlaubt, aber schlechter Stil, wenn sich die Methoden nur durch die Reihenfolge der Parameter-Typen unterscheiden. // korrekt, aber schlechter Stil public float add(int a, float b) { return a + b; } public float add(float a, int b) { return a + b; } 8 Beispiel Klasse Information Die Methode Ausgabe der Klasse Information soll verschiedenartige Daten ausgeben. class Information { public String Ausgabe(String s, int a) { return String.Format("{0}: {1}", s, a); } public String Ausgabe(String s, int a, int b) { return String.Format("{0}: {1}, {2}", s, a, b); } //Syntaktisch in Ordnung, aber schlechter Stil public void Ausgabe(int a, String s) { Console.WriteLine("{0}: {1}", s, a); } //Unterscheidung nur durch Parametername public String Ausgabe(String s, int e) { return String.Format("{0}: {1}", s, e); } gleiche Funktionalität mit mehr Parameter, ok andere Funktionalität und andere Reihenfolge // Fehler!!! } 9 Methoden überschreiben 10 Virtual und Override Die Methode der Basisklasse welche durch die Methode der abgeleiteten Klasse überschrieben werden soll, muss dies durch das Schlüsselwort virtual zulassen public virtual int MachEtwas(int a) { return a + a; } Die Methode der abgeleiteten Klasse, welche die Methode der Basisklasse überschriebt, muss die gleiche Signatur haben und das Überrschreiben durch das Schlüsselwort override anzeigen public override int MachEtwas(int a) { return a * a; } 11 Beispiel Methode MachEtwas BasisKlasse mit Methode MachEtwas() class BasisKlasse { public virtual int MachEtwas(int a) { return a + a; } } AbgeleiteteKlasse mit überschriebener Methode MachEtwas() class AbgeleiteteKlasse:BasisKlasse { public override int MachEtwas(int a) { return a * a; } } 12 Benutzen von überschriebenen Methoden static void Main(string[] args) { BasisKlasse b = new BasisKlasse(); Console.WriteLine("BasisKlasse: {0}", b.MachEtwas(3)); AbgeleiteteKlasse a = new AbgeleiteteKlasse(); Console.WriteLine("AbgeleiteteKlasse: {0}", a.MachEtwas(3)); } 13 Nicht überschriebene Methoden Virtuelle Methoden müssen nicht zwingend überschrieben werden. class BasisKlasse { public virtual int MachEtwas(int a) { return a + a; } public virtual int MachEtwasAnderes(int a) { return 3 * a; } } class AbgeleiteteKlasse:BasisKlasse { public override int MachEtwas(int a) { return a * a; } } Die abgeleitete Klasse kann, muss aber die virtuellen Methoden der Basis-Klasse nicht zwingend überschreiben. Fehlende, nicht überschriebene Methoden werden vererbt. 14 Benutzen von nicht überschriebenen Methoden static void Main(string[] args) { BasisKlasse b = new BasisKlasse(); Console.WriteLine("MachEtwas von BasisKlasse: {0}", b.MachEtwas(4)); AbgeleiteteKlasse a = new AbgeleiteteKlasse(); Console.WriteLine("MachEtwas von AbgeleiteteKlasse: {0}", a.MachEtwas(4)); Console.WriteLine("MachEtwasAnderes von AbgeleiteteKlasse: {0}", a.MachEtwasAnderes(4)); } MachEtwasAnderes wurde in der abgeleiteten Klasse nicht überschrieben, also erbt AbgeleiteteKlasse die Methode von der BasisKlasse. 15 Überladen von Konstruktoren 16 Standard-Konstruktor Konstruktoren werden benötigt um mit dem Aufruf new ein neues Objekt dieser Klasse zu erzeugen. Konstruktoren haben denselben Namen wie die Klasse und initialisieren normalerweise die Felder des neuen Objekts. Wenn eine Klasse keinen Konstruktor enthält, wird automatisch ein leerer Standard-Konstruktor erzeugt. Beim Aufruf von new Beispiel() wird ein Objekt vom Typ Beispiel mit dem Feld id erzeugt und mit 0 initialisiert. Der leere Konstruktor ist optional und könnte weggelassen werden. class Beispiel { int id = 0; public Beispiel() { } } 17 Überladener Konstruktor Auch Konstruktoren können überladen werden. Auch für Sie gilt die gleiche Regel, dass sich die Typen der Parameter der verschiedenen Konstruktoren unterscheiden müssen. Die verschiedenen Konstruktoren können sich mit dem Schlüsselwort this() gegenseitig aufrufen. class Beispiel { private int id; private String name; public Beispiel(int i) { id = i; } public Beispiel(int i, String n) : this(i) { name = n; } } 18 Beispiel Klasse Person mit drei Konstruktoren public class Person { static private int id; private String firstName="-"; private String lastName="-"; private int year = 0; ohne Parameter public Person( ) { id++; } public Person(String f, String l) : this() { firstName = f; lastName = l; } public Person(String f, String l, int y) : this(f, l) { year = y; } zwei Parameter drei Parameter public String getInfo() { String result = String.Format("{0}: {1} {2}", id, firstName, lastName); if (year != 0) result += ", " + year; return result; } } 19 Beispiel: Mietobjekte 20 Basisklasse für Mietobjekte (Fahrzeuge) class MietObjekt { protected String type; protected double tagesMietPreis; protected double kmPreis = 0; protected MietObjekt(String t, double p) { this.type = t; this.tagesMietPreis = p; } 21 Basisklasse für Mietobjekte (Fahrzeuge) public virtual double GesamtPreis(int tage, int km) { return tagesMietPreis * tage; } public virtual String Ausgabe() { String resultat = " Typ: " + type; resultat = resultat + "\n Preis pro Tag: " + tagesMietPreis; return resultat; } } 22 Die Klasse PKW class PKW : MietObjekt { private int plaetze; // Zwei Konstruktoren public PKW(String type, double preis, int pl) : base(type, preis) { this.plaetze = pl; } public PKW(String type, double preis) : this(type, preis, 4) { } 23 Die Klasse PKW // überschreiben public override String Ausgabe() { String resultat = base.Ausgabe(); resultat = resultat + ", Sitzplaetze: " + plaetze; return resultat; } // überladen public String Ausgabe(int mietTage, int km) { double preis = base.GesamtPreis(mietTage, km); String resultat = " Mietpreis für " + mietTage + " Tage: CHF " + preis; return resultat; } } 24 Die Klasse LKW class LKW : MietObjekt { private int nutzLast; public LKW(String type, double tPreis, double kmPreis, int last) : base(type, tPreis) { this.nutzLast = last; this.kmPreis = kmPreis; } public override double GesamtPreis(int tage, int km) { return tagesMietPreis * tage + km * kmPreis; } 25 Die Klasse LKW // überschreiben public override String Ausgabe() { String resultat = base.Ausgabe(); resultat = resultat + ", Nutzlast: "+nutzLast+"kg\n"; resultat = resultat + " Preis pro Km: CHF" + kmPreis; return resultat; } // überladen public String Ausgabe(int mietTage, int kmGefahren) { double preis = this.GesamtPreis(mietTage, kmGefahren); String res = " Mietpreis für " + mietTage + " Miettage und "; res = res + kmGefahren + " km: " + preis; return res; } } 26