Objektorientierte Programmierung (OOP)

    • Allgemein

    Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

      Objektorientierte Programmierung (OOP)

      Willkommen zu diesem kleinen Tutorial über generelle Prinzipien der OOP.

      Zuerst einmal Was ist OOP überhaupt ?
      Man ließt in vielen Beiträgen Dinge wie
      Benutz nicht Split() das ist VB6
      Das ist zwar unglimpflich ausgedrückt, aber meist kommt danach ein Beitrag in dieser Richtung
      ...Diese Funktion sollte nicht genutzt werden, da sie nicht OO ist.
      So was hat das auf sich ? Objektorientierung heißt, man ist auf Objekte orientiert ;)
      Nehmen wir einmal Folgendes an: Wir möchten ein Programm schreiben, das eine grafische Oberfläche (ne Form) hat und das diese sich beim darauf Klicken schließt. Jetzt gibts zwei Möglichkeiten, entweder wir stellen für alles eine Funktion bereit (CreateWindow, ShowWindow, CloseWindow, RegisterCallback usw.) und müssen damit mühsam ein Fenster erschaffen, oder wir erstellen eine Klasse die all diese Funktionen besitzt und instanzieren damit ein Objekt. Eine Klasse ist vergleichbar mit einem Bauplan. Sie beinhaltet Funktionen, Variablen, Properties usw. Wenn man jetzt ein Objekt dieses Bauplans haben möchte, in unserem Fall die Klasse Form, dann instanziert man eine solche Klasse mit dem Schlüsselwort New. Dim variable As New Form() Hier ruft das Schlüsselwort New den Konstruktor der Form-Klasse auf und variable wird eine Instanz oder ein Objekt der Klasse Form und kann benutzt werden. Dies erklärt auch warum z.B. Form2.Show() ziemlicher Schwachsinn ist, man greift auf eine Klasse also einen Bauplan zu. Der Bauplan kann keine Funktionen ausführen, er ist nurn Bauplan. Man muss folglich erst mal ein Objekt mit dem Bauplan bauen und dann .Show() aufrufen. Der Konstruktor einer Klasse ist eine Funktion, in dieser kann die Klasse irgendwelche Dinge initialisieren oder alle Möglichen Dinge vorbereiten. Der Konstruktor wird in VB.Net als Sub New() deklariert und in C# als Methode, die den selben Namen wie die Klasse selbst hat class TestKlasse {TestKlasse() {} // <- Konstruktor};. Jetzt möchte man aber 2 Fenster haben, entweder man ruft die ganzen Funktionen erneut auf oder man erstellt einfach n neues Objekt unserer Klasse. Die zweite Variente wäre Objektorientiert und nebenbei auch um einiges leichter.

      Objektorientierte Programmierung lässt sich in drei Bereiche Aufteilen: Vererbung, Kapselung und Polymorphie.

      Vererbung
      Man nehme folgendes Beispiel: Es gibt die Klasse Tier, diese hat die Methode "Talk()" und "Move()" implementiert. Nun möchten wir Klasse Hund, Katze und Maus implementieren, jetzt müsste man für Hund die 2 Methoden implementieren, für Katze und für Maus, dass ist natürlich viel zu viel Arbeit (merke: Der gute Programmierer ist faul) also schaffte hier die Vererbung abhilfe. Wenn Hund, Katze und Maus nun von Tier erben, haben sie direkt alle Methoden und fertig sind wir.
      VB

      VB.NET-Quellcode

      1. Public Class Animal
      2. Public Sub Move()
      3. Console.WriteLine("Animal is moving") ' Move()-Methode, die jedes Tier haben soll
      4. End Sub
      5. Public Sub Talk()
      6. Console.WriteLine("Animal is talking") ' Talk()-Methode, damit das Tier auch Laut geben kann
      7. End Sub
      8. End Class
      9. Public Class Dog
      10. Inherits Animal ' Hier erbt Dog (mittels dem Schlüsselwörtchen Inherits) von Animal und erbt somit alle Methoden
      11. End Class
      12. Public Class Cat
      13. Inherits Animal
      14. End Class
      15. Public Class Mouse
      16. Inherits Animal
      17. End Class

      C#

      C-Quellcode

      1. public class Animal
      2. {
      3. public void Move() // Move()-Methode, die jedes Tier haben soll
      4. {
      5. Console.WriteLine("Animal is moving");
      6. }
      7. public void Talk() // Talk()-Methode, damit das Tier auch Laut geben kann
      8. {
      9. Console.WriteLine("Animal is talking");
      10. }
      11. };
      12. public class Dog : Animal {}; // Hier erbt Dog (mittels dem ':') von Animal und erbt somit alle Methoden
      13. public class Cat : Animal {};
      14. public class Mouse : Animal {};

      Soweit so gut. Wollen wir jetzt, dass ein Hund bellt, die Katze Miaut usw. müssen wir die Methode überschreibbar, also virtuell, machen.
      VB

      VB.NET-Quellcode

      1. Public Class Animal
      2. Public Sub Move()
      3. Console.WriteLine("Animal is moving")
      4. End Sub
      5. Public Overridable Sub Talk()
      6. Console.WriteLine("Animal is talking") ' Talk() wird nun durch 'Overrideable' als überschreibbar deklariert
      7. End Sub
      8. End Class
      9. Public Class Dog
      10. Inherits Animal
      11. Public Overrides Sub Talk() ' Animal.Talk() wird durch die 'Overrides'-Anweisung überschrieben
      12. Console.WriteLine("Wuff")
      13. MyBase.Talk() ' Zusätzlich wird die Talk()-Methode der Basisklasse Animal aufgerufen, nachdem der Hund gebellt hat
      14. End Sub
      15. End Class

      C#

      C-Quellcode

      1. public class Animal
      2. {
      3. public void Move()
      4. {
      5. Console.WriteLine("Animal is moving");
      6. }
      7. public virtual void Talk() // Talk() wird nun durch 'virtual' als überschreibbar (und virtuell) deklariert
      8. {
      9. Console.WriteLine("Animal is talking");
      10. }
      11. };
      12. public class Dog : Animal
      13. {
      14. public override void Talk() // Animal.Talk() wird durch die 'override'-Anweisung überschrieben
      15. {
      16. Console.WriteLine("Wuff");
      17. base.Talk(); // Zusätzlich wird die Talk()-Methode der Basisklasse Animal aufgerufen, nachdem der Hund gebellt hat
      18. }
      19. };

      Wenn nun Dog.Talk() aufgerufen wird, wird die überschreibende Methode Dog.Talk() aufgerufen und diese ruft dann noch die überschriebene Methode der Basisklasse Animal.Talk() auf (natürlich optional). So kann man mittels Vererbung, Klassen die Methoden und Eigenschaften der Basisklasse übernehmen lassen. In anderen OO Sprachen wie C++ gibt es Mehrfachvererbung, die es einem erlaubt von mehreren Klassen zu erben und auch auf private oder protected oder public Member zuzugreifen. So kann ein Hund ein Tier sein aber auch gleichzeitig ein Haustier. Um soetwas in .Net zu lösen gibt es Interfaces diese sind "Verträge" mit der implementierenden Klasse. Er setzt vorraus das die Klasse sämtliche Methoden des Interfaces implementiert. So sind Interfaces vom Prinzip Klassen mit virtuellen abstrakten Methoden.

      Kapselung
      Kapselung bietet Schutz für Daten vom äußeren Zugriff. Jeder Hund hat ein Alter, da kann man jetzt gepflegt eine Variable anlegen Dim Age As Integer bzw. int Age;. Nur jeder kann diese Variable des Objekts verändern und z.B. den Hund verjüngen semantisch ist das natürlich Quatsch und deswegen ists zu verhindern. Hier gibts die Zugriffsmodifizierer public, private und protected. Public lässt öffentlichen Zugriff zu, Private nur Zugriff innerhalb der Klasse und Protected lässt Zugriff aus der selben oder einer erbenden Klasse zu. Zusätzlich gibt es noch Friend, welches Klassen erlaubt auf Private Member zuzugreifen. So können wir die Variable also gepflegt Private oder Protected machen Private Age As Integer bzw. private int Age;.

      Polymorphie
      Polymorphie ist die Fähigkeit einer Variablen eines Typs, ein Objekt unterschiedlichen Typs aufnehmen zu können. Bezogen auf unser Beispiel, möchten wir nun eine Collection anlegen in die all unsere Haustiere kommen, bräuchten wir eine List<> für Hund, eine für Katze und eine für Maus. Dazu kommt noch eine Funktion AddAnimal() die zu dieser Collection ein Haustier hinzufügen soll. Diese müsste auch 3mal überladen werden, kommt jetzt noch ein Goldfisch hinzu, so darf man noch ne List und Funktion hinzufügen. Eine beinahe unmenschliche Arbeit. Da alle Haustiere aber von Animal erben, können wir auch einfach eine List<Animal>.
      VB

      VB.NET-Quellcode

      1. Dim Animals As New List(Of Animal)
      2. Public Sub AddAnimal(ByVal anm As Animal) ' Fügt ein Tier zur Haustier-Liste hinzu
      3. Animals.Add(anm)
      4. End Sub
      5. ...AddAnimal(New Dog()) ' AddAnimal() wird mit einem Hund aufgerufen, so wird der Hund zum Tier (Dim hund As Animal = New Dog())
      6. ...AddAimal(New Cat()) ' So wird die Variable automatisch zum Animal (sofern seine Klasse von Animal erbt oder Animal ist)

      C#

      C-Quellcode

      1. List<Animal> Animals = new List<Animal>();
      2. public void AddAnimal(Animal anm) {Animals.Add(anm);} // Fügt ein Tier zur Haustier-Liste hinzu
      3. ...AddAnimal(new Dog()); // AddAnimal() wird mit einem Hund aufgerufen, so wird der Hund zum Tier (Dim hund As Animal = New Dog())
      4. ...AddAnimal(new Cat()); // So wird die Variable automatisch zum Animal (sofern seine Klasse von Animal erbt oder Animal ist)

      Dies nennt sich dynamische Polymorphie (da es zur Laufzeit geschiet).
      Die Polymorphie ermöglicht es auch den Typen eines Objektes zu löschen (folglich genannt Type Erasure)
      Dies ist eine Klasse die davon nutzen macht:
      VB

      VB.NET-Quellcode

      1. Class Any
      2. Private Interface IHoldBase ' Ein leeres Interface, welches hier die Eigentliche Polymorphie übernimmt
      3. End Interface
      4. Private Class Holder(Of T) ' Der "DataHolder", welcher den Datentyp per Generic bekommt (T) und IHoldBase implementiert
      5. Implements IHoldBase
      6. Public Sub New(val As T)
      7. data = val ' Variable val wird in data verstaut
      8. End Sub
      9. Public Function GetData() As T
      10. Return data ' Variable wird wieder zurückgegeben, als Typ T
      11. End Function
      12. Private data As T
      13. End Class
      14. Public Sub SetData(Of T)(val As T) ' Any.SetData(Of T)
      15. storage = New Holder(Of T)(val) ' Hier schlägt die Polymorphie zu. New Holder(Of T)(val) wird zur IHoldBase
      16. End Sub
      17. Public Function GetData(Of T)() As T
      18. Return DirectCast(storage, Holder(Of T)).GetData() ' storage wird zu T gecastet und zurückgegeben, falls storage nicht vom Typ T ist, kommt ne Exception
      19. End Function
      20. Private storage As IHoldBase
      21. End Class
      22. Dim i As New Any()
      23. i.SetData(Of Integer)(8) ' Ein Integer vom Typ IHoldBase (verstaut im Objekt i der Klasse Any)
      24. MessageBox.Show(i.GetData(Of Integer)().ToString()) ' IHoldBase wird zum Holder(Of Integer) und die Variable wird zurückgegeben.
      C#

      C-Quellcode

      1. class Any
      2. {
      3. private interface IHoldBase { }; // Ein leeres Interface, welches hier die Eigentliche Polymorphie übernimmt
      4. private class Holder<T> : IHoldBase // Der "DataHolder", welcher den Datentyp per Generic bekommt (T) und IHoldBase implementiert
      5. {
      6. public Holder(T val) {data = val;} // Variable wird in data verstaut
      7. public T GetData() { return data; } // Variable wird wieder zurückgegeben, als Typ T
      8. private T data;
      9. };
      10. public void SetData<T>(T val) { storage = new Holder<T>(val); } // Hier schlägt die Polymorphie zu. new Holder<T>(val) wird zur IHoldBase
      11. public T GetData<T>()
      12. {
      13. return ((Holder<T>)storage).GetData(); // storage wird zu T gecastet und zurückgegeben, falls storage nicht vom Typ T ist, kommt ne Exception
      14. }
      15. private IHoldBase storage;
      16. };
      17. Any i = new Any();
      18. i.SetData<int>(8); Ein int vom Typ IHoldBase (verstaut im Objekt i der Klasse Any)
      19. MessageBox.Show(i.GetData<int>().ToString()); // IHoldBase wird zum Holder<int> und die Variable wird zurückgegeben.

      C++ (mein eigentliches Orginal)

      C-Quellcode

      1. class any
      2. {
      3. private:
      4. class hold_base {public: virtual ~hold_base() {};}; // Interface hold_base als polymorpher Typ
      5. template <typename T>
      6. class data_holder : public hold_base // data_holder<> stored eine Variable vom Typ T und gibt diese auch wieder zurück
      7. {
      8. public:
      9. data_holder(const T& val) : data(val) {};
      10. const T& get_data() const {return data;};
      11. private:
      12. const T data;
      13. };
      14. public:
      15. template <typename T>
      16. any(const T& val) : storage(new data_holder<T>(val)) {}; // Template-Konstruktor initialisiert hold_base* storage mit T const& val (aufm Heap)
      17. template <typename T>
      18. const T& get_data() const
      19. {
      20. data_holder<T>* holder = dynamic_cast<data_holder<T>*>(storage.get()); // storage wird zu data_holder<T> gecastet bei Erfolg zurückgegeben, bei Misserfolg wird eine Exception geschmissen
      21. if(holder)
      22. return holder->get_data();
      23. else
      24. throw invalid_argument("Template type isn't equal to the stored type");
      25. };
      26. private:
      27. shared_ptr<hold_base> storage; // hold_base* storage hier als SmartPointer damit man die Daten nicht selbst löschen muss
      28. };
      29. int main(int argc, char* argv[])
      30. {
      31. int f = 9;
      32. any i(f); // bzw. any<int> i(f);
      33. any s(string("Hallo")); // bzw. any<string> f("Hallo");
      34. try
      35. {
      36. cout << i.get_data<int>() << endl; // Gibt "9\n" aus
      37. cout << s.get_data<string>() << endl; // Gibt "Hallo\n" aus
      38. cout << i.get_data<long>() << endl; // i beinhaltet ein int, also wird eine Exception geworfen
      39. }
      40. catch (const exception& e)
      41. {
      42. cerr << e.what() << endl;
      43. }
      44. return 0;
      45. }


      Das ist der grobe Überblick über die drei Pfeiler der OOP. Bei Fragen oder wichtigen Ergänzungen, möge derjenige sprechen ^^

      Grüße

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Gonger96“ ()

      @Gonger96: Bitte füge noch ein paar Kommentare in deine Code-Beispiele mit ein, damit sie für die Zielgruppe deines Tutorials wirklich 100%ig zu verstehen sind. Danke;)

      Gonger96 schrieb:

      oder wir erstellen eine Klasse die all diese Funktionen besitzt und instanzieren damit ein Objekt
      Für dich und mich ist hier klar, was du meinst, aber ich glaube ein Anfänger, der wirklich noch nie was davon gehört hat, wird mit den Wörtern "Klasse" und "Objekt" nichts anfangen können (zumindest nicht in dem hier gefragten Sinne). Es wäre also von Vorteil, wenn du hier zunächst mal erklärst, was eine Klasse und ein Objekt im Genaueren ist, weil darauf baut schließlich dein ganzes Tutorial auf.
      Find ich gut,

      du könntest noch auf weitere Konzepte eingehen, wie Abstraktion, also Klassen und Prototypen (wäre noch nen Schritt weiter) und auch Persistenz und Feedback wäre noch schön. Gehört ja nun mal zu OOP dazu.

      Eventuell noch eine Liste an Sprachen, die OOP unterstützen fände ich gut.

      Gruß Trade
      #define for for(int z=0;z<2;++z)for // Have fun!
      Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

      Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

      Gonger96 schrieb:

      Polymorphie ist die Fähigkeit eines Objektes verschiedene Typen anzunehmen.
      Das ist so nicht richtig - ein Objekt kann seinen Datentyp nicht ändern.
      Polymorphie ist die Tatsache, dass durch Vererbung und Interfaces allgemeinere und speziellere Datentypen geschaffen werden. Im Beispiel die Dog-, Cat-, Mouse- Klassen sind Spezialisierungen der allgemeineren Animal-Klasse.
      Nun kann eine Variable des allgemeineren Types problemlos (auch) Objekte der spezielleren Unter-Typen aufnehmen, also eine Variable vom Typ Animal kann sowohl ein Cat- als auch ein Dog- Objekte aufnehmen, und in einer List(Of Animal) kann man Hunde, Katzen und Mäuse fröhlich durcheinander-mischen.

      Es gibt zusätzlich noch statische Polymorphie, die über Klassenvorlagen und Funktionsvorlagen (Templates) läuft (während der Compilezeit). Diese ist in .Net aber fast garnicht vorhanden und deswegen werde ich darauf auch nicht weiter eingehen.
      Was heißt "fast garnicht"?
      Ich denke, statischer Polymorphie, Klassenvorlage, Template gibts in .Net überhaupt nicht.
      Daher fände ichs besser, und für Anfänger leichter verständlich, wenn diese Dinge hier auch garnicht auftauchten.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

      @Artentus
      Stimmt, der Begriff ist wahrscheinlich nicht unbedingt allen Anfängern geläufig. Werd ich dazunehmen.

      @ErfinderDesRades
      Die Polymorphie ist die Fähigkeit eines Objektes verschiedene Typen anzunehmen. Hierbei kann ein Objekt vom Typ Cat als Typ Animal deklariert werden und bleibt trotzdem noch ne Katze. Dim a As Animal = New Cat() Dabei muss es ja nicht immer Sinnvoll nur durch Vererbung (und Interfaces) laufen, wenn noch Templates mit einbezogen werden, kann ein Objekt vom Typ Cat auch als Any (ála Type Erasure) deklariert werden. Obwohl die zwei Klassen garnichts mit einander zutun haben. Ein Objekt kann seinen Datentyp natürlich nicht wechseln, aber während der Entwicklungszeit kann ein Objekt mit unterschiedlichen Typen deklariert werden.

      Templates gibts in .Net nicht, glaub ich zumindest. Dafür gibts Generics, die zwar bescheuert unflexibel sind und nicht viel bieten, aber statische Polymorphie ist dadurch trotzdem möglich. Sowas passiert meist unbewusst und viele Entwickler sind sich darüber nicht im Klaren. Verwirrend für Anfänger ists wahrscheinlich wirklich, ich werds herausnehmen ^^
      Ein Problem ist wohl auch, dass nicht klar definiert ist, was ein Datentyp, eine Variable, und was ein Objekt ist.
      Der Sprachgebrauch ist dabei tatsächlich nicht einheitlich, daher müsstest du fürs Tutorial eine Definition vorgeben.

      Gonger96 schrieb:

      Die Polymorphie ist die Fähigkeit eines Objektes verschiedene Typen anzunehmen.
      Ist jedenfalls falsch, wie gesagt. Egal nach welcher Definition - ein Objekt hat nur einen Datentyp, und nimmt keine verschiedenen an.
      Richtig ist, das Variablen Objekte aufnehmen (nicht annehmen), und eine allgemeiner deklarierte Variable kann auch Objekte spezielleren Datentyps aufnehmen.
      Hierbei kann ein Objekt vom Typ Cat als Typ Animal deklariert werden und bleibt trotzdem noch ne Katze.
      Ah - Offensichtlich wirfst du Variable und Objekt durcheinander: Das gesagte gilt so ungefähr für Variablen. Genau richtig wäre: Die Variable bleibt vom Typ Animal, und das Objekt darin bleibt vom Typ Cat.

      Und noch weitere genauere Begriffsbestimmung ist erforderlich, nämlich die Unterscheidung von Deklaration (Datentyp einer Variablen festlegen), Erzeugung eines Objektes, und Initialisierung/Zuweisung.

      VB.NET-Quellcode

      1. Dim a As Animal = New Cat()

      Hier hamwas grad: Die Variable a ist als Animal deklariert, ein Objekt wird mit New als Datentyp Cat erzeugt, und der Variablen zugewiesen.
      Man kann hier ein bischen trennen:

      VB.NET-Quellcode

      1. Dim a, b As Animal 'Deklaration
      2. a = New Cat() 'Erzeugung und Zuweisung
      3. b = a 'Zuweisung
      Erzeugung und Zuweisung kann nicht getrennt werden, denn ohne Zuweisung ist das Objekt ja nicht mehr zugreifbar.

      Dabei muss es ja nicht immer Sinnvoll nur durch Vererbung (und Interfaces) laufen, wenn noch Templates mit einbezogen werden, kann ein Objekt vom Typ Cat auch als Any (ála Type Erasure) deklariert werden.
      Keine Ahnung, wovon du sprichst - ich fänds wirklich besser, in einem Tut auf Vb-Paradise auch bei VB zu bleiben - meinetwegen noch c#, aber keine weiteren Sprachen wie c++ oder sonstwas zu behandeln
      Ein Objekt kann seinen Datentyp natürlich nicht wechseln, aber während der Entwicklungszeit kann ein Objekt mit unterschiedlichen Typen deklariert werden.
      Der erste Satz ist richtig (egal wie Objekt definiert ist), der zweite Satz widerspricht dem ersten, und ist schon deshalb falsch.
      Ein Objekt kann nur einmal deklariert werden, und natürlich kann ühaupt nur zur Entwicklungszeit deklariert werden - zur Laufzeit sollen die deklarierten Objekte laufen ;)

      ErfinderDesRades schrieb:

      Richtig ist, das Variablen Objekte aufnehmen (nicht annehmen), und eine allgemeiner deklarierte Variable kann auch Objekte spezielleren Datentyps aufnehmen.

      Genauso meinte ichs auch. Ich bin da anscheinend an der Formulierung gescheitert :S

      VB.NET-Quellcode

      1. Dim a As Animal = New Cat()

      Jetzt hab ich die Variable a vom Typ Animal und ihr wird ein Cat-Objekt zugewiesen, dass auch noch instanziert wird. Dann hab ich oben "Objekt" einmal für Objekt und einmal für Variable benutzt und irgendwie durcheinander geworfen.

      Keine Ahnung, wovon du sprichst - ich fänds wirklich besser, in einem Tut auf Vb-Paradise auch bei VB zu bleiben - meinetwegen noch c#, aber keine weiteren Sprachen wie c++ oder sonstwas zu behandeln

      C++ wird nirgendwo behandelt ;) , hab nur ebend die Klasse hinzugefügt da in der VB.Net übersetzung etwas n bisschen anders läuft. Die Klasse Any ist in C# und VB vertreten und macht gebrauch von Type-Erasure. Guck dir die Klasse mal an. So kann man z.B. einen String

      VB.NET-Quellcode

      1. Dim s As New Any()
      2. s.SetData(Of String)("Hallo")

      In einer Klasse unterbringen und dabei den Typen löschen. Any beinhaltet nen String ohne zu Wissen, dass es ein String ist. Man könnte in ner List<Any> alles Mögliche unterbringen. Das ist ein Anwendungsbeispiel der Polymorphie, gibts nicht nur in C++ sondern auch in .Net.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

      Das kompilliert bei mir ohne Probleme. Du musst dir eigentlich nur die Klasse kopieren da machts bei mir keine Probleme.
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Module Module1
      2. Class Any
      3. Private Interface IHoldBase ' Ein leeres Interface, welches hier die Eigentliche Polymorphie übernimmt
      4. End Interface
      5. Private Class Holder(Of T) ' Der "DataHolder", welcher den Datentyp per Generic bekommt (T) und IHoldBase implementiert
      6. Implements IHoldBase
      7. Public Sub New(val As T)
      8. data = val ' Variable val wird in data verstaut
      9. End Sub
      10. Public Function GetData() As T
      11. Return data ' Variable wird wieder zurückgegeben, als Typ T
      12. End Function
      13. Private data As T
      14. End Class
      15. Public Sub SetData(Of T)(val As T) ' Any.SetData(Of T)
      16. storage = New Holder(Of T)(val) ' Hier schlägt die Polymorphie zu. New Holder(Of T)(val) wird zur IHoldBase
      17. End Sub
      18. Public Function GetData(Of T)() As T
      19. Return DirectCast(storage, Holder(Of T)).GetData() ' storage wird zu T gecastet und zurückgegeben, falls storage nicht vom Typ T ist, kommt ne Exception
      20. End Function
      21. Private storage As IHoldBase
      22. End Class
      23. Sub Main()
      24. Dim a As New Any()
      25. a.SetData(Of String)("Hallo")
      26. ' Jetzt ist in a der String "Hallo" gestored der Typ String ist allerdings futsch
      27. Console.WriteLine(a.GetData(Of String))
      28. End Sub
      29. End Module
      Ja, habs jetzt auch gefunden - du hast selbst eine Any-Klasse gecodet - ich hab gedacht, solch gebe es im Framework (weil Any war früher ein Schlüsselwort, und ich dachte, vlt. in 2013 hamses wieder eingeführt) 8|

      Würde ich weglassen. Typen können nicht gelöscht werden. Das Codebeispiel ist ja auch komplett sinnfrei - was soll ein leeres Interface? Imo kannste mit so Kram nur Verwirrung stiften.

      Wollte ich deine Any-Klasse vereinfachen, so machte ich so:

      VB.NET-Quellcode

      1. Class Any
      2. Public Sub SetData(Of T)(val As T)
      3. storage = val
      4. End Sub
      5. Public Function GetData(Of T)() As T
      6. Return DirectCast(storage, T) ' storage wird zu T gecastet und zurückgegeben, falls storage nicht vom Typ T ist, kommt ne Exception
      7. End Function
      8. Private storage As Object
      9. End Class
      10. Dim i As New Any()
      11. i.SetData(8) ' geht sogar ohne Typangabe
      12. MessageBox.Show(i.GetData(Of Integer)().ToString()) ' intern wird auf den TypParameter gecastet
      13. 'Noch einfacher ganz ohne Any-Unfug:
      14. Dim i As Object = 8
      15. MessageBox.Show(DirectCast(i, Integer).ToString()) ' statt intern iwas rumzucasten kann ich auch gleich direckt casten
      Wie gesagt: ist zwar möglich, aber ist Unfug, also weglassen.
      Nützlich wäre allenfalls eine Verlinkung auf Artentus' Tutorial zum Thema Typumwandlung. Also falls du meinst, Typumwandlung müsse noch ins Tutorial hinein.
      Imo müsste aber eher "Überladung" hinein, weil den Begriff hast du verwendet, und kein wirklicher Anfänger wird auch nur ahnen können, was das sein soll.
      @ErfinderDesRades
      Ahhhg Mist. Die Hauptaufgabe der Klasse ist eigentlich, dass man Methoden dem Interface hinzufügt und so Zugriff auf eine Methode unterschiedlicher Typen erreicht. Ich wollte dir grad n Beispiel fertig machen, aber anscheinend kann ich auf Methoden von Generics nicht aufrufen :S

      Ich hatte das eigentlich versucht aus C++ herüber zu portieren, da gehts nämlich. Naja trotzdem ist es eine andere Art von Polymorphie, oder zumindest ein anderer Weg. Bei deinem Beispiel machst du ja hier von der Basisklasse Object gebrauch, das tu ich nicht. Gäbe es die Klasse nicht, würde meine Klasse trotzdem funktionieren.

      Gonger96 schrieb:

      Bei deinem Beispiel machst du ja hier von der Basisklasse Object gebrauch, das tu ich nicht. Gäbe es die Klasse nicht, würde meine Klasse trotzdem funktionieren.

      Wenn es die Klasse Object nicht gäbe? Und dass, obwohl alles von Object erbt??? 8|

      Gonger96 schrieb:

      aber anscheinend kann ich auf Methoden von Generics nicht aufrufen
      Wie sollte das auch funktionieren? Ich kenn mich in solchen C++-Geschichten nicht wirklich aus, aber nach der Logik von VB und C# wäre sowas Latebinding. Wenn du das vernachlässigst, dann ist es Pflicht, dass der Compiler mit Sicherheit sagen kann, dass ein Member existiert. Bei Genericas wird dies durch Typeinschränkungen gegeben, also du kannst angeben, was das übergebene Objekt auf jeden Fall ist. Da du hier aber jeden beliebigen Typen angeben können möchtest, kannst du keine Typeinschränkung angeben, was wiederum zur Folge hat, dass die einzigen mit Sicherheit vorhandenen Member die von Object sind, weil ein Object ist es auf jeden Fall (alles ist ein Object).
      In C++ wird das über statische Polymorphie gelöst ^^
      Naja du kannst auf alles Mögliche eines Template-Parameters zugreifen. Zur Compilezeit wird nun der Typ T für jeden Aufruf durch den dortigen Typen ersetzt und eine eigene Funktion generiert. Falls der Typ Methode XY() nicht besitzt, kommt schlicht und ergreifend ein Compilerfehler. Das ist einer der Unterschiede zwischen Templates und Generics. Hier ist ein Überblick über die Unterschiede.

      In C++ gibts auch noch Variadic Templates, das sind Templates mit einer unbegrenzten Anzahl. So kann man Methoden mit einem oder 200 Parametern aufrufen. Templates sind 100% flexibel, was auch Type-Traits ermöglicht. Man kann von mir aus 3^n per Templates zur Compilezeit berechnen, dagegen scheinen Generics iwie als Notlösung zu fungieren :S

      Bei Generics kann man angeben, dass der Typ von einer bestimmten Klasse erben muss, oder n Interface implementieren muss. Bei Templates kann man per Traits und Policies alles über den Templatetypen regeln. Z.B. Du möchtest das der Typ ein Pointer ist hier würde man mit dem Trait anfangen:

      C-Quellcode

      1. template <typename T>
      2. struct is_ptr
      3. {
      4. static const bool value = false;
      5. };
      6. template <typename T>
      7. struct is_ptr<T*>
      8. {
      9. static const bool value = true;
      10. };

      So ist T kein Pointer nimmt der Compiler die erste struct, ist T ein Pointer so stimmt die zweite eher überein. is_ptr<int>::value ist folglich = false is_ptr<int*>::value ist true.
      Nun geht man hin und baut sich eine Struct, die einen Template-Fehler verursachen kann

      C-Quellcode

      1. template <bool test, typename Returntype = void>
      2. struct enable
      3. { // typedef type fehlt
      4. };
      5. template <typename Returntype>
      6. struct enable<true, Returntype>
      7. {
      8. typedef Returntype type;
      9. };

      enable<true> hat die typedef type, enable<false> jedoch nicht. Hat man jetzt eine gewöhnliche Funktion

      C-Quellcode

      1. template <typename T>
      2. typename enable<is_ptr<T>::value>::type // Rückgabetyp
      3. testfunc(const T& val)
      4. {
      5. //...
      6. }
      und T ist ein Pointer, so ist is_ptr<T>::value = true und enable<...>::type = void, also alles korrekt. Ist T kein Pointer ist is_ptr<T>::value = false und enable<...>::type existiert nicht -> die Folge ein Fehler. IntelliSense sagt
      Keine Instanz von Funktionsvorlage "testfunc" stimmt mit der Argumentenliste überein