Entwickler-Ecke

C# - Die Sprache - Verständnisfragen zur OOP


Dingo - So 05.03.17 13:37
Titel: Verständnisfragen zur OOP
Hallo an alle!

Ich hänge gerade an den Grundlagen von C# und habe noch ein paar Verständnisfragen zur OOP, da die Beispiele für mich etwas weit hergeholt sind und ich diese nur in den Grundzügen verstehe. Ist ein etwas längerer Text, doch hoffe ich das ihr einem Anfänger ein wenig helft es zu Verstehen.

Bisher habe ich alles mit Variablen, primitiven Dateitypen und auch Methoden so weit, so guterstanden.

Jedoch bei Namensraum, Klassen und Objekten laufe ich gedanklich immer wieder gegen eine Mauer. Habe nun schon einige Bücher und einige Forenbeiträge gelesen, aber verstehe es einfach nicht. Ich hab mir jetzt alles noch mal in Ruhe vorgenommen und mir selbst eine Erklärung mit Beispielen gemacht, die eher meiner Denkweise entsprechen und wollte mal Fragen, ob ich das nun so richtig verstanden habe.

Methoden:

Methoden dienen dazu wiederkehrende Aufgaben bzw. Probleme zu lösen. Zum Beispiel eine simple Berechnung in einer Methode abarbeiten und mir über „return“ einen weiterverwenden Wert zurück geben oder eben eine Ausgabe erledigen.

Zum Beispiel:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
double versandkosten(double preis) {
     return 2.0 * anzahl;
}

void ausgabe() {
     Console.WriteLine(„Dies ist eine Ausgabe“);
}

namespace:

Namensräume sind Hüllen in denen verschiedene Klassen und Interfaces unter dem selben Gesichtspunkten zusammengefasst werden. Hier finde ich das Java Wort Packages besser, weil greifbarer.

Beispiel wäre demzufolge:

C#-Quelltext
1:
2:
3:
4:
5:
6:
namespace Spieler {
     class Bewegung {}
     class Verteitigung {}
     class Angriff {}
     class Schaden {}
}

Der Spielercharakter hätte nun seinen eigenen Namensraum mit den für ihn nutzbaren Klassen. Neben diesen Namensraum gibt es noch einen Namensraum namens „Gegner“:

C#-Quelltext
1:
2:
3:
4:
5:
6:
namespace Gegner {
     class Bewegung {}
     class Verteitigung {}
     class Angriff {}
     class Schaden {}
}


Es sind wieder die selben Klassen zu finden, jedoch in einem anderen Namensraum mit einem Bezug auf den Gegner und nicht auf den Spieler. Beide Räume sind von einander getrennt, außer man greift auf sie durch „using“ zu.


Klasse:

Eine Klasse ist eine Zusammenfassung von Objekten mit gleichen Merkmalen.

Ich nehme hier noch mal den Spieler zur Hilfe als Beispiel:

C#-Quelltext
1:
2:
3:
4:
5:
6:
class Bewegung {
     methode vorwärtsLaufen();
     methode rückwärtsLaufen();
     methode linksLaufen();
     methode rechtsLaufen();
}


Von Klassen können nun auch Kinder abgeleitet werden, es wird vererbt, sagt man ja.

Eine Vererbung wäre nun zum Beispiel:

C#-Quelltext
1:
class Rennen : Bewegung                    

Rennen würde nun also alles von Bewegung erben, das einzige was nun verändert werden würde, wäre die Laufgeschwindigkeit, ansonsten muss er sich ja immer noch vorwärts, rückwärts, ect. Bewegen, was er ja von Bewegung geerbt hat..


Objekte:

Objekte sind Dinge um uns herum, sie können Fähigkeiten (Methoden) und Eigenschaften (Variablen) besitzen. Das Objekt Kugelschreiber hätte nun die Eigenschaften (Variablen) 20g schwer, blau und 12 cm lang. Das Objekt Kugelschreiber hätte nun die Fähigkeit (Methode) zu schreiben.

Bis hier hin nun alles ja ganz logisch, aber wann mache ich das:

C#-Quelltext
1:
Gegner newBoss = new Gegner();                    

Was bringt es mir ein Objekt zu erzeugen? Erzeuge ich damit wie im Beispiel aus der Klasse Gegner einen neuen Kämpfer oder wie ist Objekt erzeugen nun zu verstehen? Wann nutze ich es sinnvoll an?

Nebenbei das hier wäre nun ein Construlor oder?

C#-Quelltext
1:
Gegner newBoss = new Gegner();                    


Static:

Die Erklärung zu „static“ hab ich bisher gar nicht verstanden, wann nutzt man es und für was ist es sinnvoll?


This:


Hier herrscht auch ein Verständnissproblem. Wann und wie nutze ich das und auf was greift es zu?


Instanz:

Instanzen sind Objekte, verstehe ich das richtig? Sprich es ist nur ein anderes Wort für ein und das selbe?


Vielen Dank schon einmal für die Hilfe!

Moderiert von user profile iconTh69: C#-Tags hinzugefügt
Moderiert von user profile iconTh69: Titel geändert (war "Fragen eines C#-Anfängers").


Delphi-Laie - So 05.03.17 18:29

user profile iconDingo hat folgendes geschrieben Zum zitierten Posting springen:
Instanzen sind Objekte, verstehe ich das richtig? Sprich es ist nur ein anderes Wort für ein und das selbe?


Instanzen sind Variablen eines Objekttypes, einer Objektklasse. Mit leichtem Zögern kann ich diese Frage bejahen


Delete - So 05.03.17 20:33

- Nachträglich durch die Entwickler-Ecke gelöscht -


OlafSt - Mo 06.03.17 09:38

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconDingo hat folgendes geschrieben Zum zitierten Posting springen:
Instanzen sind Objekte, verstehe ich das richtig? Sprich es ist nur ein anderes Wort für ein und das selbe?


Instanzen sind Variablen eines Objekttypes, einer Objektklasse. Mit leichtem Zögern kann ich diese Frage bejahen


Ich nicht. Nicht einmal mit etwas zögern.

Das supersimple Beispiel mit dem Clown macht es deutlich: public class Clown ist die Klasse. Es ist nur die Definition gemeint. Bei Clown clown1 = new Clown(); ist clown1 eine Instanz der Klasse Clown. Genauso wie etwas weiter unten clown2 ebenfalls eine Instanz der Klasse Clown ist.

Ergo: Klasse -> Nur die Definition, quasi ein Variablentyp. Ähnlich wie Integer oder Double.
Instanz -> Tatsächlich existierende Variablen einer Klasse.


Delete - Mo 06.03.17 10:08

- Nachträglich durch die Entwickler-Ecke gelöscht -


Ralf Jansen - Mo 06.03.17 11:05

Zitat:
Instanzen sind Variablen eines Objekttypes, einer Objektklasse. Mit leichtem Zögern kann ich diese Frage bejahen

Zitat:
Instanz -> Tatsächlich existierende Variablen einer Klasse.

Man könnte bei euren Beschreibung den Eindruck gewinnen Variablen wären die Instanzen einer Klasse. Möglicherweise habt ihr Variable umgangssprachlich verwendet aber Variablen haben in der Programmierung natürlich eine konkrete Bedeutung. Eine Variable ist nur ein Verweis auf etwas. Dieses etwas kann eine Instanz sein muß aber nicht.

C#-Quelltext
1:
Clown clown = null;                    

Der Verweisegedanke ist wichtig. Mehrere Variablen könnten ja auch auf die gleiche Instanz einer Klasse verweisen.

C#-Quelltext
1:
2:
Clown clown = new Clown();
Clown clown2 = clown;  // 2.ter Verweis aber gleiche Instanz


Ergo eine Variable ist keine Instanz von etwas. Eine Variable ist ein Verweis auf etwas.


Dingo - Mo 06.03.17 14:15

Vielen Dank für die Ausführlichen Hilfe, jetzt versteh ich die Zusammenhänge langsam! :)


Ralf Jansen - Mo 06.03.17 14:34

Zitat:
namespace:
....
Es sind wieder die selben Klassen zu finden, jedoch in einem anderen Namensraum mit einem Bezug auf den Gegner und nicht auf den Spieler. Beide Räume sind von einander getrennt, außer man greift auf sie durch „using“ zu.

Die Klassen in den verschiedenen Namespaces haben technisch erstmal nichts miteinander zu tun. Logisch stellst du sie möglicherweise in Beziehung oder durch andere technische Maßnahmen wie der Implementierung der gleichen Interfaces oder der Ableitung von einer gemeinsamen Basisklasse. Aber nur weil sie in verschiedenen Namespaces dann gleich heißen wäre das erstmal nur Zufall aber kein Zusammenhang in irgendeiner Weise.

Durch "using" bekommst du keinen Zugriff du machst den Zugriff nur einfacher. Vergleichen wir das mit üblichen Namen von Personen. Der Einfachheit meinen ;) Überall Ralf Jansen zu schreiben ist aufwendig viel einfacher wäre einfach nur Ralf zu sagen weil es hier ja auch eher selten zu Namenskollisionen mit anderen Ralfs kämme. Programmiertechnisch hieße das wir definieren vorher ein using Jansen; um dann im folgenden einfach immer nur Ralf sagen zu müssen. Genau das ist die Kernaufgabe des using. Der Name einer Klasse ist eigentlich immer inklusive seines Namespaces. Bei dir also eigentlich Spieler.Bewegung bzw. Gegner.Bewegung. Und weil das aufwendig ist das immer auszuschreiben kannst du halt sagen using Spieler, am besten nur an Stellen wo du nicht auch Gegner brauchst, um im folgenden einfach Bewegung sagen zu können.

Ergo Namespaces sind dazu da eindeutige Namen für Klassen zu bekommen. Ab einigen Tausend Klassen wie zum Beispiel im .Net Framework wird es nämlich schwer die eindeutig zu halten ohne sie künstlich mit Bestandteilen zu ergänzen die wenig hilfreich wären. "Using" hilft dann das man denn vollen Namen einer Klasse nicht immer nennen muß sondern die Kurzfassung reicht solange die dann noch eindeutig sind. Würdest du in deinem Beispiel sowohl using Gegner; also auch using Spieler; in einer Datei verwenden und dann versuchen die Klasse Bewegung anszusprechen (also die Kurzfassung ohne Namespace angabe) würde dir der Compiler das anmeckern mit dem Hinweis das der Name der Klasse so nicht eindeutig bestimmbar ist.


Delphi-Laie - Mo 06.03.17 14:38

Gut, wenn man es reduktionistisch betrachtet, dann ist ein Objekt nur ein Zeiger, eine Speicheradresse, also eine Integerzahl. Doch hilft eine solche Antwort dem Fragesteller?

Nun bin ich mir auch nicht sicher, ob Objekte in allen Programmiersprachen Zeiger sind. Ist es nicht auch grundsätzlich möglich, daß Variablen von Objekttypen /-klassen "echte" Variablen sind, ohne diese elenede Verzeigerung?

Ich habe es so gelernt, daß Objekte zunächst als Typen (in etwas ausführlicher Form als Klassen) deklariert werden und jede Variablendeklaration (-definintion?) davon eine Variable, eine Instanz ist. Schließlich steht ja das Schlüsselwort "var" auch vor den Objektvariablen, und der Typ ist zudem kein Pointer, sondern eben der des Objektes! Was daran nun so grottenfalsch sein soll, verschließt sich mir.


jaenicke - Mo 06.03.17 15:22

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
und jede Variablendeklaration (-definintion?) davon eine Variable, eine Instanz ist.
Das ist der große Unterschied. In zwei unterschiedlichen Variablen kann ein Verweis auf die gleiche Instanz bzw. das gleiche Objekt liegen. Deshalb ist das nicht gleichbedeutend. Denn beide Variablen verweisen nur auf die gleiche Instanz im Speicher, sind selbst aber nicht gleich.


Ralf Jansen - Mo 06.03.17 15:22

Grottenschlecht ist sicher nicht das was ich sagen wollte. Spätestens wenn wir uns mit Folgeerklärungen rumschlagen also z.B. den Unterschied zwischen struct und class oder sprachunabhängiger benannt zwischen Referenz- und Value-Typen wird es schwer. Oder auch die Frage wie genau jetzt der Transport einer Instanz eines Klasse über einen Methodenaufruf funktioniert (wann Referenz wann Kopie und warum ...) wird es schwer das klar zu machen wenn man vorher bei einem Anfänger ein Gedankenmodell aufgebaut hat das eine Gleichsetzung von Variablen und Instanzen beinhaltete. Ja beim lehren kann man nicht immer gleich den Lernenden mit allen Details konfrontieren und man hat immer den Punkt des "Lügens für Anfänger" und wie weit man das treibt. Hier halte ich es aber für relevant diesen Unterscheidung frühzeitig zu benennen da er nachträglich schwer aus dem Kopf zu bekommen ist. Zumindest ist das meine Annahme, mein eigener Lernprozess disbezüglich ist schon zu lange her ;)

Zitat:
Nun bin ich mir auch nicht sicher, ob Objekte in allen Programmiersprachen Zeiger sind.


Zeiger sind ein reines Implemetierungsdetail ob hinter etwas einer steckt oder nicht sollte irrelevant sein. In allen mir bekannten OO Sprachen wird aber eine Variable ein Verweis/Referenz sein und nicht das Ding an sich. Valuetypen tun nur so als ob es diesen direkten Zusammenhang gäbe (wie ignorieren hier auch wieder besser das Implementierungsdetail der Unterscheidung von Heap und Stack).


Dingo - Di 07.03.17 11:34

Ich merk schon, ich merk schon, ich hab noch viel Arbeit vor mir, um alles zu verstehen.^^

Also das "using" nutzt man dann sozusagen auf Deutsch aus Gründen der "Faulheit"?!

Ich könnte schreiben:

C#-Quelltext
1:
System.Console.WriteLine();                    


Einfacher und kürzer wäre es aber wenn ich oben einen Verweis setze:

C#-Quelltext
1:
using System;                    


Und im Quellcode an sich nur noch schreibe:

C#-Quelltext
1:
Console.WriteLine();                    


Verstehe ich das so richtig?

Dann hätte ich noch einmal eine Verständnisfrage zu dem kleinen Miniabschnitt hier:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lektion2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Erste Ausgabe des Programms in Lektion 2");
            Console.ReadLine();
        }
    }
}


Wenn ich es verstehe wäre nun:

System = Namespace, genau so wie Lektion2
Console = Wäre damit eine Klasse von System
WriteLine = Wäre demnach eine Methode von Console, die wiederrum zu System gehört

Passt das so?

Und vielen Dank für die Hilfe, ihr helft mir echt weiter!!!

Moderiert von user profile iconTh69: B- durch C#-Tags ersetzt


Ralf Jansen - Di 07.03.17 11:44

Zitat:
Verstehe ich das so richtig?


Ja.

Zitat:
Passt das so?


Ja .. sprachlich vielleicht nicht ganz. Bei Namespace spricht man eher davon das sich etwas in ihm befindet. Man würde alse eher sagen "Die Klasse Console befindet sich im Namespace System". Dein Satz ist da nicht falsch klingt aber zumindest unüblich.

Übrigens dein Beispiel enthält nebenbei einen sinnvollen Einsatz von static.


Dingo - Di 07.03.17 11:49

Super, endlich verstanden.

Zu "static", nun ja, static main besagt ja, dass dies die erste Methode ist, die beim Programmstart initialisiert wird.

Würde ich nun andere Methoden nun auch mit "static" versehen, würde die dann auch zum Programmstart mit los legen?


jfheins - Di 07.03.17 12:46

Nein ...

Es ist so: Der Anfang des Programms wird durch den Methodennamen vorgegeben - die Methode muss immer Main heißen.
Nun ist es so, dass ganz am Start ja noch keine Objekte existieren. Klassen ja, aber eben noch keine Objekte.

Wenn Main() nun eine normale Methode der Klasse Program wäre, dann müsste man die ja erst instanziieren! Das widerspricht aber dem Gedanken, dass Main() zuerst aufgerufen wird. Daher macht man Main() static, sodass die Methode auch direkt auf die Klasse aufgerufen werden kann.

Ohne static wäre das so ein Henne-Ei-Problem: Um Objekte zu erzeugen muss das Programm laufen und um das Programm laufen zu lassen müsste man Objekte erzeugen.

Hier ist noch eine SO-Antwort dazu: http://stackoverflow.com/questions/11332494/why-should-main-be-static
Man kann also auch eigenen Code vor Main() ausführen. Aber es wurde damals einfach so definiert, dass es eine statische Main-Methode geben muss ;-)
Zitat:
Main wird in einer Klasse oder einer Struktur deklariert. Main muss statisch sein und darf nicht Öffentlich sein. (Im Beispiel oben erhält sie den Standardzugriff private.) Die einschließende Klasse oder die Struktur muss nicht statisch sein.
Von hier: https://msdn.microsoft.com/de-de/library/acy3edy3.aspx


Dingo - Di 07.03.17 12:58

Verstehe!^^

Ich sitze gerade an diesem Beispiel hier (endlich die Codefunktion des Forums gefunden^^):


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lektion2
{
    class Program
    {
        static string ToString(int value)
        {
            return "" + value;
        }
        
        static void Main(string[] args)
        {
            string s = Program.ToString(123456);
            Console.WriteLine(s);
        }
    }
}


Also an dem Beispiel mal, ToString ist eine Methode, die sofort beim Start aufgerufen wird, benötigt dafür ein static.
Den Code an sich versteh ich schon, nur warum ich ein static davor schreiben muss, wird mir immer noch nicht klar. :?

Es ist doch eigentlich ein normaler Methodenaufruf in der Klasse Program?

Muss man es so sehen, dass eigentlich eine Methode zu einem Objekt gehört und so lange es noch kein Objekt gibt, eigentlich auch keine dazugehörige Methode gestartet werden könnte?


Ralf Jansen - Di 07.03.17 13:08

Ohne static an ToString brauchst du eine Instanz der Klasse Program um die aufzurufen.
Also dann


C#-Quelltext
1:
2:
Program program = new Program();
program.ToString(123456);


Da aber an der Instanz keine weiteren Informationen hängen die man aus der Instanz holen müßte damit ToString das richtige macht wäre static hier richtig. Denn alles was die ToString Methode braucht um seine Funktion auszuführen bekommt es übergeben. Es besteht also keinen Bedarf für eine Instanz bzw. weitere Instanz der Klasse Program.


Dingo - Di 07.03.17 13:27

Ok und wäre es nicht möglich ToString gleich auszuführen, innerhalb der Main Methode, ohne extra über eine Methode zu gehen? Oder macht man sämtliche Berechnungen und der gleichen über Methoden mit Rückgabewert?

EDIT:

Gut, ich hab noch mal in nem anderen Buch nachgelesen, demnach ist Static ein Feld, sprich also eine Variable oder Methode, die keinem bestimmten Objekt zugehörig ist, sondern einer Klasse angehört, also nach Buch eine Klassenvariable ist...

Noch nicht ganz einleuchtend aber schon klarar...

Würde also bedeuten:
Eine Klassenvariable bekommt einen Wert in der Klasse zugeordnet?
Eine Instanzvariablen bekommt einen Wert im Objekt zugeordnet?


Ralf Jansen - Di 07.03.17 14:52

Zitat:
Würde also bedeuten:
Eine Klassenvariable bekommt einen Wert in der Klasse zugeordnet?
Eine Instanzvariablen bekommt einen Wert im Objekt zugeordnet?


Ja. Es wird möglicherweise klarer wofür man das braucht wenn du bedenkst das es die Klassenvariable nur einmal gibt während du natürlich ganz viele Instanzen der selben Klasse erzeugen kannst und jede Instanz dann eine eigene Instanzveraiable mit ihrem eigene Wert haben kann.

Darum einen Programmeinstiegspunkt brauchst du genau einmal und das auch eindeutig auffindbar darum ist static das Richtige für die Main Methode. Hättest du eine Klasse die eine Person beschreibt, dann wäre ein Feld das den Geburtstag der Person beschreibt besser nicht statisch denn jede Person(jede Instanz der Klasse Person) ist potentiell an einem anderen Tag geboren worden. Würdest du aber zum Beispiel ein Feld an dieser Klasse haben das beschreibt mit welchem Alter diese Person Volljährig wird/wurde dann könntest du überlegen ob dieses Feld nicht statisch sein sollte. Weil diese Info für alle Personen gleich ist.


Dingo - Di 07.03.17 15:06

Könnte man also sagen, dass Klassenvariablen "Globale Variablen" sind, die überall einsetzbar sind und überall den selben Wert besitzen?


Ralf Jansen - Di 07.03.17 15:15

Globale Variablen habe so einen nicht objektoriebtierten Klang und waren auch schon vorher eher ein Zeichen von schlechter Programmierung. Deshalb würde ich es nicht so nennen. Letztlich hängen ja an Klassen/Methoden/Properties/feldern etc. noch Sichtbarkeiten (public, private etc.). Bei Leuten die globalen Variablen ~denken~ spielt solche Dinge meist keine Rolle und führen dann zu schlechtem Code. Aus Anfängersicht kann man es aber so sehen das etwas statisches leichter zu erreichen ist als etwas das an einer Instanz hängt. Es ist etwas ~globaler~ ja. Diese Sichtweise wird aber ab einer bestimmten Projektgröße problematisch.


Delphi-Laie - Di 07.03.17 15:24

user profile iconDingo hat folgendes geschrieben Zum zitierten Posting springen:
Also das "using" nutzt man dann sozusagen auf Deutsch aus Gründen der "Faulheit"?!


Damit werden Bibliotheken eingebunden, also modulare Programmierung ermöglicht.

Mit Faulheit allein hat das kaum noch etwas zu tun. Zum einen kann man nicht ständig das Rad neu erfinden, die Zeit hat man nicht, es ist auch unproduktiv. Zum anderen, wenn man wirklich versuchen wollte, die Funktionalität nachzubilden oder gar aus dem Quellext der Bibliothek zu "borgen" (also zu kopieren, was eigentlich nicht erlaubt ist), wird man kaum erfolgreich sein, weil es zuviele Abhängigkeiten untereinander gibt. Irgendwann hat man den Großteil des Modules in sein eigenes Projekt übernommen und eingebunden, und das ist soviel, daß der eigene Quelltext völlig darin intergeht.


Ralf Jansen - Di 07.03.17 15:33

Zitat:
Damit werden Bibliotheken eingebunden, also modulare Programmierung ermöglicht.


Nicht in c#/.Net. Referenzieren einer Bibliothek macht man über einen anderen Weg. Im Gegensatz zu anderen Sprachen ist ein Namespace auch nicht direkt gleichzusetzen mit einer Assembly/Package/Bibliothek und (ein solcher) using bezieht sich nur auf Namespaces. Mit der "Faulheit" hat er also nicht ganz unrecht. Netter kann man auch sagen das es es die Lesbarkeit erhöht. Immer den vollqualifizierten Namen einer Klasse zu benutzen macht Code schwer lesbar.


Delphi-Laie - Di 07.03.17 21:51

Nun, für mich war "using" analog zu "uses", zumal Borland-Guru Anders Hejlsberg ja auch C# mehr oder weniger aus der Taufe hob.

Anscheinend ist es eher mit dem Pascal-Schlüsslwort "with" gleichzusetzen. Dann stimme ich zu, daß es dem Einsparen von Tipparbeit dient (wobei ich with grundsätzlich nicht verwende).


Dingo - Mi 08.03.17 14:54

Freut mich, dassa hier so geholfen wird! :)

Kommen wir mal zu Konstruktormethoden, ist in meinem Buch etwas verwirrend beschrieben, mal sehen ob ich es richtig verstanden habe, nehmen wir mal folgenden Quellcode:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lektion3
{
    // Die Basisklasse für die Flugzeuge, auch generelle Klasse genannt
    class Luftfahrzeug {
        // Variablendeklaration
        public string kennung;

        // Konstruktormethode 1, OHNE Parameter:
        public Luftfahrzeug(){
        }

        // Konstruktormethode 2, MIT Parameter:
        public Luftfahrzeug(string kennung){
            this.kennung = kennung;
        }

        // Methodendeklaration
        public void Steigen(int meter){
            Console.WriteLine(kennung + " steigt " + meter + " Meter");
            Console.ReadLine();
        }

        public void Sinken(int meter){
            Console.WriteLine(kennung + " steigt " + meter + " Meter");
            Console.ReadLine();
        }

    }
    
    // Flugzeug, eine Unter-Klasse von Luftfahrzeuge
    class Flugzeug : Luftfahrzeug{

        public Flugzeug(){
        }
    }



    class Program
    {
        static void Main(string[] args)
        {
            // Ruft Konstruktormethode MIT Parameter auf
            Luftfahrzeug flieger = new Luftfahrzeug("LH 4080");
            flieger.Steigen(100);
            flieger.Sinken(50);

            // Ruft Konstruktormethode OHNE Parameter auf
            Flugzeug flieger2 = new Flugzeug();
            flieger2.Steigen(333);
        }
    }
}


So, in der Basisklasse sind zwei Konstruktormethoden, eines mit Argument, eines ohne Argument.

Nun habe ich zwei Flugzeuge, eines welches über die Klasse Luftfahrzeuge als Objket definiert wurde und ein Flugzeug welches über Flugzeug als Objekt definiert wurde.

Wenn ich in der Klasse class Flugzeug : Luftfahrzeug{} habe, die KEINE Konstruktormethode hat, wird, so hab ich es verstanden ja automatisch die Konstruktormethode der Basisklasse angesprochen.

Das selbe wenn ich in der Konstruktormethode einen Parameker habe. Wenn ich in class Flugzeug : Luftfahrzeug{} keine Konstruktormethode mit einem Parameter habe, wird autoamatisch zur Basisklasse gegangen und da eine Konstruktormethode gesucht, zu der mein Parameter passt.

Nun steht in meinem Buch zu diesem Beuspiel:


C#-Quelltext
1:
2:
3:
class Flugzeug : Luftfahrzeug{
     public Flugzeug(){
}


Zitat: Diese Konstruktormethode tut nichts, ihr Aufruf hat daher keine weitre Folgen als den Aufruf der Konstruktormethode der Basisklasse. Sie können aber im Konstruktor-Programmblock durchaus Anweisungen schreiben. In Frage käme z.B. die Festlegung, dass die Varaible „kennung“ (Klasse Luftfahrzeuge“) initialisiert wird:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
class Flugzeug : Luftfahrzeug 
     {
     class Flugzeug()
     {
          kennung = „keine Kennung“;
     }
}


So viel bis hier her und nun bin ich etwas ratlos.

Es ist schon klar, dass wenn ich in meiner Klasse class Flugzeug : Luftfahrzeug keine Konstruktormethode habe, automatisch die der Basisklasse genutzt wird, jedoch: Warum wird die Baisklassenkonstruktormethode auch aufgerufen, wenn ich doch schon eine Konstruktormethode in class Flugzeug : Luftfahrzeug habe, oder versteh ich da was falsch?


Ralf Jansen - Mi 08.03.17 15:29

Zitat:
Warum wird die Baisklassenkonstruktormethode auch aufgerufen, wenn ich doch schon eine Konstruktormethode in class Flugzeug : Luftfahrzeug habe, oder versteh ich da was falsch?


Da kommt das mit den Sichtbarkeiten ein wenig ins Spiel. Jedes Mitglied einer Klasse (egal ob Feld,Property,Methode) Klasse hat eine eigene Sichtbarkeit und die gilt auch zwischen Ableitungen. Wenn also die Klasse Luftfahrzeug private Mitglieder hätte die initialisiert werden müßten kann man das nicht direkt von Flugzeug aus erledigen es muß von Luftfahrzeug aus passieren.

Nemmen wir uns dein Beispiel und stellen uns vor das das Feld kennung in der Luftfahrzeug Klasse privat wäre (das wäre eh richtiger und kannst du auch am besten selbst probieren) dann wirst du feststellen das die Lösung kennung im Konstruktor von Flugzeug zu setzen nicht mehr funktioniert. Das wäre auch im allgemeinen besser. Es gibt dann weniger stellen von wo aus kennung geändert werden kann das führt zu weniger Abhängigkeiten die zu leichter handhabbaren Code führen (das nur so, mußt du jetzt nicht nachvollziehen können das kommt später mit etwas Erfahrung).

Wie sollte man also kennung setzen? Nun die Luftfahrzeug Klasse hat dafür bereits einen Konstruktor den man aufrufen sollte der das kann. Du hast bereits bemerkt das der parameterlose Konstruktor aufgerufen wird und du das nicht verhindern kannst. Was du aber kannst ist steuern welcher Konstruktor benutzt wird. Es ist also so das immer einer Konstruktor benutzt wird in jeder Klasse deiner Vererbungshierarchie um zu erzwingen das alles richtig initialisiert wird du hast aber Einfluß welche Konstruktoren aufgerufen werden. Dazu gibt es die Schlüsselwort base das man an den Konstruktor hängt.

Also anstatt so


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
class Flugzeug : Luftfahrzeug 
{
    public Flugzeug() // Hier wird implizit auch der parameterlose Konstruktor von Luftfahrzeug aufgerufen. Der parameterlose Konstruktor wird immer benutzt wenn man keinen anderen benennt.
    {
        kennung = "keine Kennung";
    }
}

solle man dann

C#-Quelltext
1:
2:
3:
4:
5:
class Flugzeug : Luftfahrzeug 
{
    public Flugzeug() : base("keine Kennung")
    {}
}


benutzten. Base bezeichnet die Konstruktoren der Vorfahrklasse und da hier ein string übergeben wird also der Konstruktor mit dem string Parameter in der Vorgfahrklasse aufgerufen anstatt den parameterlosen,


Dingo - Mi 08.03.17 15:48

Verstehe, was ist aber wenn ich nun die Klasse Düsenflugzeuge nehme.

Ich initialisiere nun mal ein Düsenflugzeug:


C#-Quelltext
1:
2:
Düsenflugzeuge flieger4 = new Düsenflugzeuge("LH 999");
flieger4.Steigen(999);


Demzufolge würde nun dieser Konstruktor aufgerufen:


C#-Quelltext
1:
2:
3:
4:
5:
class Düsenflugzeuge : Flugzeug{
     // Konstruktormethode
     public Düsenflugzeuge(string kennung) : base(kennung){
     }
}


Der Konstruktor, was macht er?

Du schriebst, er würde nun wieder die nächst höhere Klasse ansprechen? Also dann diese:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
class Flugzeug : Luftfahrzeug{

     // Konstruktormethode OHNEParamete      
     public Flugzeug(){
     }

     // Konstruktormethode MIT Parameter
     public Flugzeug(string kennung) : base(kennung){
     }
}


Diese wiederrum wird nun Luftfahrzeuge ansprechen?


Ralf Jansen - Mi 08.03.17 15:57

Zitat:
Diese wiederrum wird nun Luftfahrzeuge ansprechen?


Genau. Man kann keine Ebene überspringen, also von Düsenflugzeuge aus direkt den Konstruktor von Luftfahrzeug zu benutzten ist nicht erlaubt. Auf jeder Vererbungsebene muß mindestens 1 Konstruktor benutz werden.


Dingo - Mi 08.03.17 16:03

Ah, langsam versteh ichs. Also wird damit:


C#-Quelltext
1:
2:
public Düsenflugzeuge(string kennung) : base(kennung){
}


Noch genauer gesagt damit:


C#-Quelltext
1:
base(kennung)                    


So lange die Methodendeklaration weiter in der Klassenhirarchie nach oben gereicht, bis de Variable string kennung gefunden wird und die Methodendeklaration ausgefhrt werden kann?

Bedeutet also, so lange die Variable die ich verwenden möchte in der nächst höheren Ebene ist, muss ich : base nutzen?


Ralf Jansen - Mi 08.03.17 19:14

user profile iconDingo hat folgendes geschrieben Zum zitierten Posting springen:


C#-Quelltext
1:
base(kennung)                    


So lange die Methodendeklaration weiter in der Klassenhirarchie nach oben gereicht, bis de Variable string kennung gefunden wird und die Methodendeklaration ausgefhrt werden kann?



Nein. base heißt Vorfahr und aus dem übergebenen Type, hier string, weiß der Compiler dann das er im Vorfahren den Konstruktor mit einem string aufzurufen hat. Wenn es einen solchen nicht gibt wirst du bei einem Compiliervorgang eine entsprechende Fehlermeldung bekommen. Er wird nicht weitersuchen zum Beispiel im Vor-Vorfahren ob es dort einen solchen Konstruktor gibt. Dann wären wir ja wieder an dem Punkt das eine Ebene ausgelassen wird beim Konstruktorenaufruf udn esmuß für jede Klasse in der Vererbungshierarchie ein Konstruktor benutzt werden.

Zitat:
Bedeutet also, so lange die Variable die ich verwenden möchte in der nächst höheren Ebene ist, muss ich : base nutzen?


Jein es geht um die Konstruktoren nicht Variablen. Wenn der entsprechende Konstruktor die Variable setzt dann ist das das gleiche aber da gibt es ja keinen Zwang es so programmiert zu haben.


Dingo - Do 09.03.17 09:32

OK, dann noch mal ne Frage dazu:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lektion3
{
    class Luftfahrzeug
    {
        public string kennung;
        public string test;

        // Konstruktormethode 1:
        public Luftfahrzeug(){
            Console.WriteLine("Basisiklasse");
        }

        // Konstruktormethode 2:
        public Luftfahrzeug(string kennung, string test){
            this.kennung = kennung;
            this.test = test;
           }
    }

    class Flugzeuge : Luftfahrzeug{
        public Flugzeuge(){
            Console.WriteLine("Subklasse");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Ruft Konstruktormethode MIT Parameter auf
            Flugzeuge flieger = new Flugzeuge();
            Console.WriteLine(flieger.kennung + flieger.test);
            Console.ReadLine();

        }
    }
}


In diesem Beispiel habe ich mal die Erzeugung eines Objektes ohne Parameter probiert. Es wird über die Klasse Flugzeuge aufgerufen. Nun meine Frage wäre, warum startet in der Basisklasse ebenfalls der Konstruktor ohne Methode und nicht nur der in der Subklasse? Hat dies eine spezielle Bewandtnis, warum starten beide und gilt dies bei Parametern auch, wenn sowohl Basis- als auch Subklasse nur einen String als Parameter haben?

Der Output sehe ja dann so aus:

Basisklasse
Subklasse


Th69 - Do 09.03.17 10:02

Mir scheint, du hast das Prinzip doch noch nicht so ganz verstanden.
Es wird explizit oder implizit immer ein Basis-Konstruktor aufgerufen, und zwar immer genau der, welcher die passende Signatur (d.h. die passenden Parameter) hat.

In deinem Beispiel erzeugt der Compiler eigentlich folgenden Konstruktor, weil du explizit keinen Basisklassenkonstruktor aufgerufen hast:

C#-Quelltext
1:
2:
3:
4:
public Flugzeuge() : base()
{
    Console.WriteLine("Subklasse");
}

Es wird also der Basisklassen-Konstruktor Luftfahrzeug() aufgerufen. Diesen - ohne Parameter - nennt man auch Standardkonstruktor (default constructor).
Es gibt nämlich noch den Fall, daß man selbst gar keinen Konstruktor bei einer Klasse definiert hat - und dann erzeugt der Compiler automatisch einen öffentlichen Standardkonstruktor dafür (weil sonst könnte man gar kein Objekt dieser Klasse erzeugen).

Und jetzt lese am besten noch mal die Antworten von user profile iconRalf Jansen durch.


Delete - Do 09.03.17 10:15

- Nachträglich durch die Entwickler-Ecke gelöscht -


Ralf Jansen - Do 09.03.17 10:51

Zitat:
Schade, das Clowns Beispiel war doch nicht zielführend. Hätte man es noch einfacher machen können?


Auch bei Codebeispielen gilt es gibt kein "One size fits all". Einfacheres Beispiel also möglicherweise aber eben leider nicht passend für jeden.


OlafSt - Do 09.03.17 10:57

Ihr alle verschweigt die Geheimnisse ;)

Nehmen wir eine einfachst-Klasse:

C#-Quelltext
1:
2:
3:
4:
public class Auto
{
   public void Starten() { }
}


Diese Klasse hat nur eine Methode, Starten genannt. Keine Properties, keine Konstruktoren, nix.

Wenn ich nun eine Instanz davon erstellen will, schreibe ich folgenden Code:

C#-Quelltext
1:
   public Auto MyCar = new Auto();                    


Ganz offensichtlich wird da ein Konstruktor aufgerufen - aber meine Klasse hat doch gar keinen ?!?!?

Das Geheimnis: Jede Klasse hat einen Konstruktor. Einen, der public ist (sonst wäre er nicht aufrufbar) und keine Parameter erwartet. Hat, wie in meinem Beispiel, eine Klasse keinen Konstruktor, wird klammheimlich einer von C# erzeugt. Und dann hat die Klasse einen Konstruktor und der kann und wird dann aufgerufen, wenn man eine Instanz erzeugt.

In diesem Konstruktor ist dann entsprechender Code drin, der Speicher für die Klasse reserviert, Felder anlegt usw. Dieser Code wird bei Aufruf eines jeden Konstruktors ausgeführt, auch wenn man selbst einen oder mehrere davon definiert hat. Er ist sozusagen "versteckt".

Und jetzt bekommt das ganze Sinn: Im Beispiel mit den Luftfahrzeugen und Flugzeugen wird bei

C#-Quelltext
1:
Flugzeuge flieger = new Flugzeuge();                    

logischerweise der Konstruktor von Flugzeuge aufgerufen. Da aber Flugzeuge von der Klasse Luftfahrzeuge abstammt, muß zunächst dessen Konstruktor aufgerufen werden, denn wir müssen ja erstmal Speicher für die Basisklasse besorgen, bevor wir den Rest an Speicher für die Subklasse besorgen können. Darum werden die Konstruktoren allesamt aufgerufen - auch ohne base()-Angabe.


Dingo - Do 09.03.17 11:02

Also "static" hab ich schon halbwegs verstanden, komplett verstehen werd ichs wohl erst mit genügend Übungen und die sind in meinem Lehrbuch echte Mangelware, alles sehr Trocken. Zum "base", ich dachte echt ich habs langsam und langsam werd ich immer verwirrter um so länge rich über "base" nachdenke.^^"

Zum Clown Beispiel, das liegt nicht an dir bzw. deinem Beispiel direkt, Frühlingsrolle. Etwas versteh ich es schon, aber zum Beispiel das nicht:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
Clown[] clowns = { clown1, clown2, clown3 };
msg = "";
foreach (Clown c in clowns)
{
 msg += string.Format("{0} is type of {1}\n", c.Name, c.GetType().Name);
}
MessageBox.Show(msg);


Man wird in meinem Buch recht trocken sofort in Objekte und Klassen geworfen. Was nun Typen oder primitive Typen sind, Methoden, Array und so weiter und so viel, wird zwar schon gebraucht, zumindest teilweise, steht aber erst viel weiter hinten im Heft! Demzufolge brauche ich ziemlich leichte Beispiele, dich ich mit meinem jetzigen Wissensstand lesen kann. Was z.B. foreach ist oder GetType, weiß ich leider noch nicht. Windows Forms hab ich noch gar nicht genutzt, alles läuft bisher über Konsolenanwendung.

Hab mir jetzt mal zum Verständniss ein Test-Programm zusammen geschustert, um es besser zu verstehen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
namespace Lektion3
{
    class Luftfahrzeug
    {
        public string kennung;

        // Konstruktormethode 1:
        public Luftfahrzeug(){
            Console.WriteLine("Basisiklasse");
        }

        // Konstruktormethode 2:
        public Luftfahrzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Basisiklasse mit Parameter");
           }
    }

    class Flugzeuge : Luftfahrzeug{
        public Flugzeuge(){
            Console.WriteLine("Test1");
        }

        public Flugzeuge(string kennung) : base(kennung){
            Console.WriteLine("Test2");
           }
    }

    class Düsenflugzeug : Flugzeuge {
        public Düsenflugzeug(){
            Console.WriteLine("Test3");
        }

        public Düsenflugzeug(string kennung) : base(kennung){
            Console.WriteLine("Test4");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Ruft Konstruktormethode MIT Parameter auf
            Düsenflugzeug flieger = new Düsenflugzeug("Testung");
            Console.WriteLine(flieger.kennung);
            Console.ReadLine();

        }
    }
}


Was passiert?

Als Output bekomme ich:
Basisklasse mit Parameter
Test2
Test4
Testung der Kennung

Es wird also ein Objekt Flieger erzeugt, welches von der Klasse Düsenflugzeuge abstammt. Als Parameter haben wir den String "Testung der Kennung". Dieser String soll an die Variable "Kennung" übergeben werden, welche ja Mitglied der Basisklasse Luftfahrzeuge ist.

Da Flieger zur Klasse der Düsenflugzeuge gehört, wird nun aus der Basisklasse des Objektges Flieger der passende Konstruktor gesucht:


C#-Quelltext
1:
2:
3:
public Düsenflugzeug(string kennung) : base(kennung){
     Console.WriteLine("Test4");
}


Dieser Konstruktor ruft nun wieder seine Base auf:


C#-Quelltext
1:
2:
3:
public Flugzeuge(string kennung) : base(kennung{
     Console.WriteLine("Test2");
}


Der wiederrum ruft nun seinen Konstruktor auf:


C#-Quelltext
1:
2:
3:
4:
public Luftfahrzeug(string kennung){
     this.kennung = kennung;
     Console.WriteLine("Basisiklasse mit Parameter");
}


Im letzten Konstruktor wird nun der Befehl nicht mehr weiter gegeben, sondern an die Basisklasse.

So, das ganze hätte man auch so schreiben können:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
namespace Lektion3
{
    class Luftfahrzeug
    {
        public string kennung;

        // Konstruktormethode 1:
        public Luftfahrzeug(){
            Console.WriteLine("Basisiklasse");
        }

        // Konstruktormethode 2:
        public Luftfahrzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Basisiklasse mit Parameter");
        }
    }

    class Flugzeuge : Luftfahrzeug{
            public Flugzeuge(){
            Console.WriteLine("Test1");
        }

        public Flugzeuge(string kennung){
            Console.WriteLine("Test2");
        }
    }

    class Düsenflugzeug : Flugzeuge
    {
        public Düsenflugzeug(){
            Console.WriteLine("Test3");
        }

        public Düsenflugzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Test4");
        }
    }

    class Program
    {
        static void Main(string[] args){
            // Ruft Konstruktormethode MIT Parameter auf
            Düsenflugzeug flieger = new Düsenflugzeug("Testung");
            Console.WriteLine(flieger.kennung);
            Console.ReadLine();

        }
    }
}


Mit


C#-Quelltext
1:
this.kennung = kennung;                    


greife ich auf die string Kennung zu und weiße ihr einen Wert zu.

Das Ergebnis sieht nun anders aus:

Basisklasse
Test1
Test4
Testung

Mal zur Erinnerung wo alles mit base geschrieben wurde:

Basisklasse mit Parameter
Test2
Test4
Testung

Der große Unterschied ist ja nun, dass im ersten Test alle Konstruktormethoden mit Parameter angesprochen wurden, im zweiten Test nur die erste Konstruktormethode mit Parameter, dann alle Konstruktormethoden ohne Parameter.

Also wird im Endeffekt, wenn wir nun mal von einen Konstruktor ohne Parameter ausgehen, immer der Konstruktor der darvor war mit angesprochen, mit man zur obersten Basisklasse kommt, den Standardkonstruktor.

EDIT:

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Und jetzt bekommt das ganze Sinn: Im Beispiel mit den Luftfahrzeugen und Flugzeugen wird bei

C#-Quelltext
1:
Flugzeuge flieger = new Flugzeuge();                    

logischerweise der Konstruktor von Flugzeuge aufgerufen. Da aber Flugzeuge von der Klasse Luftfahrzeuge abstammt, muß zunächst dessen Konstruktor aufgerufen werden, denn wir müssen ja erstmal Speicher für die Basisklasse besorgen, bevor wir den Rest an Speicher für die Subklasse besorgen können. Darum werden die Konstruktoren allesamt aufgerufen - auch ohne base()-Angabe.


Das wusste ich, es gibt einen unsichtbaren Konstruktor der immer da ist. Also wird der Speicherplatz für mein neues Objekt im Endeffekt nicht von meinem Konstruktor an sich reserviert, sondern dies macht dann der oberste Konstruktor, der Standardkonstruktor und deshalb werden alle Zwischen-Konstruktor aufgerufen, weil der Standardkonstruktor demnach der Ausgangspunkt wäre?
Über meine anderen Konstruktoren gebe ich ihm dann nur den Befehl?

Man könnte also sagen mit "base" lenke ich den Konstruktor in eine bestimmte Richtung?

EDIT 2:

Langsam wirds her Wirt:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
namespace Lektion3
{
    class Luftfahrzeug
    {
        public string kennung;

        // Konstruktormethode 1:
        public Luftfahrzeug(){
            Console.WriteLine("Basisiklasse");
        }

        // Konstruktormethode 2:
        public Luftfahrzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Basisiklasse mit Parameter");
        }
    }

    class Flugzeuge : Luftfahrzeug{
            public string test;
            public Flugzeuge(){
            Console.WriteLine("Test1");
        }

        public Flugzeuge(string kennung){
            Console.WriteLine("Test2");
        }

        public Flugzeuge(string FLP1, string FLP2){
            test = FLP1;
            kennung = FLP2;
            Console.WriteLine(test + kennung);
        }

    }

    class Düsenflugzeug : Flugzeuge
    {
        public Düsenflugzeug(){
            Console.WriteLine("Test3");
        }

        public Düsenflugzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Test4");
        }

        public Düsenflugzeug(string FLP1, string FLP2) : base(FLP1, FLP2)
        {
            Console.WriteLine("Test5");
        }
    }

    class Program
    {
        static void Main(string[] args){
            // Ruft Konstruktormethode MIT Parameter auf
            Düsenflugzeug flieger = new Düsenflugzeug("FlugzeugParameter1""FlugzeugParameter2");
            //Console.WriteLine(flieger.kennung);
            Console.ReadLine();

        }
    }
}


Hab nun mal den Spaß mit zwei Parametern auf unterschiedlichen Klassenebenen gemacht.

Aufgerufen würde also zuerst das werden:


C#-Quelltext
1:
2:
3:
public Düsenflugzeug(string FLP1, string FLP2) : base(FLP1, FLP2){
            Console.WriteLine("Test5");
}


Was wiederrum die Basisklasse aufrufen würde:


C#-Quelltext
1:
2:
3:
4:
5:
public Flugzeuge(string FLP1, string FLP2){
     test = FLP1;
     kennung = FLP2;
     Console.WriteLine(test + kennung);
}


Es würde nun also der Zugriff auf die Variable test und kennung erfolgen und diese würden per Konsole ausgegeben.

Es würde nun angezeigt:

Basisklasse
FlugzeugParameter1FlugzeugParameter2
Test5

Schreiben häte man es wieder auch gleich wieder so:


C#-Quelltext
1:
2:
3:
4:
5:
public Düsenflugzeug(string FLP1, string FLP2){
     test = FLP1;
     kennung = FLP2;
     Console.WriteLine("Test5");
}


Delete - Do 09.03.17 12:20

- Nachträglich durch die Entwickler-Ecke gelöscht -


Dingo - Do 09.03.17 12:24

Da ich schon wieder net mal weiß was get, virtual und override ist und wie man es anwendet, kann ich es wieder mal nur halb verstehn.

Mal abgesehen davon, sowohl Luftfahrzeuge als auch Flugzeuge haben die selbte Variable die als Privat deklariert ist, je mit einem anderen Wert.


Delete - Do 09.03.17 12:39

- Nachträglich durch die Entwickler-Ecke gelöscht -


Dingo - Do 09.03.17 12:52

Es muss doch auch ohne virtal und override zu erklären sein. Hab so schon Probleme das zu verstehen und nun kommt noch mehr dazu um nur Konstruktormethoden zu verstehen.^^"

Lieg ich denn so dermaßen daneben mit meinen Verstädniss?

Was sagt MSDN dazu:

Zitat:
Mit dem base-Schlüsselwort kann innerhalb einer abgeleiteten Klasse auf Member der Basisklasse zugegriffen werden:

- Rufen Sie eine Methode der Basisklasse auf, die durch eine andere Methode überschrieben wurde.

- Geben Sie an, welcher Konstruktor der Basisklasse beim Erstellen von Instanzen der abgeleiteten Klasse aufgerufen werden soll.

Die Basisklasse, auf die zugegriffen wird, ist die in der Klassendeklaration angegebene Basisklasse. Wenn Sie z. B. class ClassB : ClassA angeben, wird auf die Member von ClassA aus ClassB zugegriffen, unabhängig von der Basisklasse von ClassA.


Das hab ich auch verstanden.

In meinem Buch steht:

C#-Quelltext
1:
2:
3:
4:
5:
public Flugzeug : Luftfahrzeug{
     public Flugzeug(){
          kennung = "keine Kennung";
     }
}

Wäre das selbe wie:

C#-Quelltext
1:
2:
public Flugzeug(string kennung) : base(kennung){
}

Eben nur eine andere Schreibweise.

Nahmen wir uns nun noch einmal das vor:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
namespace Lektion3
{
    class Luftfahrzeug
    {
        public string kennung;

        // Konstruktormethode 1:
        public Luftfahrzeug(){
            Console.WriteLine("Basisiklasse");
        }

        // Konstruktormethode 2:
        public Luftfahrzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Basisiklasse mit Parameter");
        }
    }

    class Flugzeuge : Luftfahrzeug{
        public string test;

        public Flugzeuge(){
            Console.WriteLine("Test1");
        }

        public Flugzeuge(string kennung){
            Console.WriteLine("Test2");
        }

        public Flugzeuge(string FLP1, string FLP2){
            test = FLP1;
            kennung = FLP2;
            Console.WriteLine(test + kennung);
        }

    }

    class Düsenflugzeug : Flugzeuge
    {
        public Düsenflugzeug(){
            Console.WriteLine("Test3");
        }

        public Düsenflugzeug(string kennung){
            this.kennung = kennung;
            Console.WriteLine("Test4");
        }

        public Düsenflugzeug(string FLP1, string FLP2){
            test = FLP1;
            kennung = FLP2;
            Console.WriteLine(test + kennung);
            Console.WriteLine("Test5");
        }
    }

    class Program
    {
        static void Main(string[] args){
            // Ruft Konstruktormethode MIT Parameter auf
            Düsenflugzeug flieger = new Düsenflugzeug("FlugzeugParameter1""FlugzeugParameter2");
            //Console.WriteLine(flieger.kennung);
            Console.ReadLine();

        }
    }
}


Wo wäre hier nun der Fehler, rein Fehler bringt er mir nicht.

Ich löse den Konstruktor aus:


C#-Quelltext
1:
2:
3:
4:
5:
6:
public Düsenflugzeug(string FLP1, string FLP2){
    test = FLP1;
    kennung = FLP2;
    Console.WriteLine(test + kennung);
    Console.WriteLine("Test5");
}

Wäre dies falsch, da der Member test eigentlich zu Flugzeuge gehört und kennung zu Luftfahrzeug?

Moderiert von user profile iconTh69: C#-Tags hinzugefügt


OlafSt - Do 09.03.17 13:05

Nein. Du machst nur den siebten Schritt vor dem zweiten. Und wie im realen Leben ist dieser Spagat einfach ne Nummer zu heftig.

Ohne ein gerütteltes Maß an Basics und Theorie wird das heutzutage nix mehr. Lerne also, was es mit public und private und virtual auf sich hat. Wir beantworten ja gerne deine Fragen, können aber so große Lücken (und Spagate) nicht überbrücken.


Delete - Do 09.03.17 13:11

- Nachträglich durch die Entwickler-Ecke gelöscht -


Dingo - Do 09.03.17 13:14

Zitat:
Nein. Du machst nur den siebten Schritt vor dem zweiten. Und wie im realen Leben ist dieser Spagat einfach ne Nummer zu heftig.

Ich eher weniger, dass gibt mir mein Studienbuch vor, zu dem ich in ner Woche ne Arbeit schreiben soll...

Zitat:
Lerne also, was es mit public und private und virtual auf sich hat. Wir beantworten ja gerne deine Fragen, können aber so große Lücken (und Spagate) nicht überbrücken.


private: Nur in der Klasse aufrufbar
public: Überall aufrufbar

Sorry, ich bin euch echt dankbar für die Hilfe, ich muss mich aber intelligenterweise an das Studienbuch halten, ich finds auch blöd aufebaut, weil jeder dritte Satz ist: "Dazu später mehr!"

Ich hab geschätzt 10000 Fragezeichen im Kopf und versuch die irgendwie raus zu bekommen und tollerweise muss ich in in knapp über ner Woche zu Methoden, Klassen, Objekte und unter anderem Konstruzktormethoden ne Arbeit abliefern, erst dann gehts los mit Datentypen, Verbinundgsoperatoren und so weiter.

Finds auch klasse...

--------------


C#-Quelltext
1:
2:
public Düsenflugzeug( string kennung ) : base( kennung ){
}


Müsste ich also benutzen, wenn "kennung" private wäre?

Und

C#-Quelltext
1:
this.kennung = kennung;                    

bzw.

C#-Quelltext
1:
kennung = FLP2;                    


könnte ich aus irgendeiner Konstruktormethode nutzen, wenn "kennung" public wäre?

--------------

Oder bedeutet nun einfach base, nutze den Konstruktor der Basisklasse?

--------------

Ähmmm, LOL, durch Zufall gefunden bei MSDN:

Mithilfe des base-Schlüsselworts kann ein Konstruktor den Konstruktor einer Basisklasse aufrufen. Beispiel:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
public class Manager : Employee
        {
            public Manager(int annualSalary)
                : base(annualSalary)
            {
                //Add further instructions here.
            }
        }


Jetzt wird mir das auch irgendwie klar, was ihr mit public und private meintet...

Moderiert von user profile iconTh69: C#-Tags hinzugefügt


Delete - Do 09.03.17 13:52

- Nachträglich durch die Entwickler-Ecke gelöscht -


Dingo - Do 09.03.17 14:01

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Geht doch. :D

Irgendwann muss es ja mal klack machen! :lol:
Jetzt versteh ich auch was ihr mit public und private meintet, wird alles gerade sehr viel logischer.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Damit kein komplettes Wirrwarr entsteht, weder hier noch bei dir, würde ich vorschlagen, dass wir hier weiterhin deine Flugzeug-Thematik behandeln.
Für Fragen die du nur zu Methoden, nur zu Eigenschaften, nur zu ... hast, wirst du ein neues Thema erstellen. Alles klar?

Mach mer so!

Und nochmal vielen vielen Dank für die Hilfe! :beer:


Dingo - Mi 29.03.17 10:42

So, mal kurz ein kleiner Beitrag. Eure Hilfe war nicht umsonst, bin bei meiner Prüfung zum ersten Teil von C# knapp an der 1 vorbei gerasselt. Hilfe hat sich also gelohnt! ^^


Delete - Mi 29.03.17 11:44

- Nachträglich durch die Entwickler-Ecke gelöscht -


Ralf Jansen - Mi 29.03.17 13:26

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Schön zu lesen. :beer: Dann bis zur nächsten Prüfung. :D


Da bin ich dann raus. Bei BWL helfe ich auf gaaarrrrr keinen Fall :hair: