Jump to content
Unity Insider Forum

Generics - Parameter Allgemein definieren? Wie geht das?


Hermetes

Recommended Posts

Hallo an Alle! :)

Die Methode soll die Möglichkeit bekommen zu erben. Das override habe ich in meinem Beispiel schon mal hinzugefügt. 
Diese Methode hätte ich gerne für andere Objekte , nicht nur für das Object Project. Also allgemein formuliert soll es werden.

Ich weiß nur Generics und <T>. Sah mir im Netz einige Beispiele an, kann es jedoch nicht in die Praxis übertragen.
Weiß das jemand?
 

 

public override bool AddData(Project data)
   {
      
      CurrentProjectID = projects.Count;
      data.projectID = CurrentProjectID++;
      projects.Add(data);
      return true;
   }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Generics und Vererbung sind erstmal zwei unterschiedliche Konzepte. In Unity kollidieren sie öfter als anderswo, aber auch nicht überall.

Beispiel für Generics:

List<Car> carList = new List<Car>();

Hier liegt keine Vererbung vor, sondern es wird bei der Variablendeklaration und bei der Objekterzeugung ein Typ für den generischen Parameter T eingesetzt. Dafür brauchst du kein override oder sonst irgendetwas.

Einen generischen Parameter definierst du an einer Klasse oder einer Methode. Benutzt du in einer Methode denselben Parameternamen wie bei der Klasse, dann ist auch derselbe Typ gemeint.

public class Thing
{
  public void DoStuff<T>()
  {
    
  }
}
public class Thing<T>
{
  public void DoStuff<T>()
  {
    
  }
}

Du kannst dann T als Stellvertreter für einen beliebigen Typen sehen und diesen dann z.B. bei einer Variablendeklaration verwenden.

public class Box<T>
{
  public T thingInTheBox;
}
var pencilBox = new Box<Pencil>();

// Geht:
pencilBox.thingInTheBox = new Pencil();

// Geht nicht:
pencilBox.thingInTheBox = new Keyboard();

Überall, wo dann T in deiner Klasse steht, wird dann sozusagen das eingesetzt, was bei der Objekterstellung angegeben wird, also hier Pencil.

Ob das hier allerdings sinnvoll ist, ist halt noch die Frage. Du kannst eventuell auch einfach eine gemeinsame Superklasse verwenden und Generics in der Kiste lassen.

class Elephant : Animal
class Giraffe : Animal
public void DoStuff(Animal animal)
{
    //...
}
DoStuff(new Giraffe())

Auch hier brauchst du kein override oder sonst irgendetwas bei der DoStuff-Methode.

Es gibt auch noch einige weitere Varianten, z.B. mit einer Superklasse für den generischen Parameter.

Aber mal ganz an den Anfang zurück... was genau willst du tun und warum willst du verschiedene Typen zulassen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi Sascha! Du hast es wieder mal geschafft das ich es verstanden habe! :)
Die Superklasse! Doch kaum hatte ich es auf die Superklasse umgestellt , fiel mir auf das ich viel mehr Structs als Class habe. Und mit Structs funktioniert das so ja gar nicht.
Ist jetzt aber gar nicht schlimm, denn ich wüsste auch gar nicht wie ich das jeweils in die richtige Liste packen könnte.

Habe mich aus einem Video "infiszieren" lassen. (Basta 2014) . In einem Beispiel ging es darum das jemand 50 Methoden für unterschiedliche Typen machte. Er verbesserte das mit einer IEnumerable<T> und einer Func . Er ersetze also eine Methode mit einer einzigen! Was mich ziemlich beeindruckte. Und leider möchte ich nicht öffentlich sagen an was für ein Projekt ich arbeite. Für Unity ist es ein ziemlich untypisches Projekt. Jedenfalls werde ich auch über 30 verschieden Objekt Typen haben. Für jedes Objekt muss ich aber 5 zusätzliche Klassen schreiben, die eigentlich fast das selbe tun. Natürlich ich könnte einfach so weiter machen, aber wenn man den Anspruch hat möglichst Allgemeingültigen Code zu schreiben, hmm fühlt sich einfach nicht richtig an. Jetzt weiß ich auch nicht du meinen Text verstehen kannst, wenn ich ja nur die Hälfte an Informationen preisgebe. 

Aber immerhin , ich bin mit deiner Antwort wieder ein Stückchen besser geworden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb Hermetes:

Und mit Structs funktioniert das so ja gar nicht.

Ein Umstand, den ich immer noch sehr bedauernswert finde. Dass mit Structs kein Polymorphismus geht ist ja schön und gut, aber lasst mich doch bitte trotzdem gemeinsame Eigenschaften in einem Super...Struct... bündeln... :(

vor 2 Stunden schrieb Hermetes:

Jetzt weiß ich auch nicht du meinen Text verstehen kannst, wenn ich ja nur die Hälfte an Informationen preisgebe.

Ja, doch - und es klingt nach einer guten Idee, was du da vorhast. Grundsätzlich kann man sagen, dass "x-Mal dasselbe machen nur jedes Mal ein kleines bisschen anders" immer schlecht und vor allem vermeidbar ist. Leider wie gesagt nicht bei Structs, aber... naja :)

Generics sind dabei aber eben nur eines von mehreren verfügbaren Werkzeugen.

In OOP ist Vererbung ein sehr mächtiges Instrument. Im Animal-Beispiel von oben kannst du z.B. eine Methode "Eat(Food food)" deklarieren und die gilt dann für alle Tiere. Wenn du also gerade eine Zoowärter-Klasse schreibst, kannst du den jedes Tier füttern lassen, weil jedes Tier "Eat" kennt. Dabei muss der Zoowärter nicht zwischen Elefanten und Giraffen unterscheiden können, sondern arbeitet nur mit der Animal-Klasse. Mit dem override-Keyword kannst du dann überschreiben, was eine Giraffe genau tut, wenn sie isst, sodass sie sich von den anderen Tieren unterscheidet.

In OOP ebenfalls sehr beliebt sind Interfaces. Vererbung stellt ein "ist-ein" Verhältnis zwischen Klassen dar; eine Giraffe "ist-ein" Tier und kann deshalb essen, weil alle Tiere das können. Anstatt das zu tun, kannst du mit einem Interface Fähigkeiten darstellen. Mit einem Interface wie diesem:

public interface IFoodConsumer
{
  void Eat(Food food);
  Poo Excrete();
}

kannst du Methoden definieren, die eine "implementierende" (statt "erbende") Klasse besitzen muss. Da die Dinger im Gegensatz zu Klassen wirklich nur diese Methodensignaturen haben und keine Zustandsfelder oder so, kannst du davon beliebig viele an deine Klassen klatschen:

public class Giraffe : IFoodConsumer, ILegRunner, IFurry

Und dein Zoowärter kann dann einfach mit ALLEN Klassen arbeiten, die das IFoodConsumer-Interface besitzen.

public void Feed(IFoodConsumer consumer)
{
  consumer.Eat(myFood);
}
zookeeper.Feed(myGiraffe);

Damit bist du dann nicht mehr auf Tiere beschränkt, da jede beliebige Klasse jedes beliebige Interface implementieren kann.

In Unity sehr präsent ist dagegen komponentenbasiertes Design. Anstatt eine Klasse zu basteln, die alle Eigenschaften und Fähigkeiten hat, hast du eine "Entity" (in Unity standardmäßig "GameObject", es sei denn du benutzt ECS). Diese Entity hat dann mehrere Komponenten, die genau wie Interfaces beliebig viele Fähigkeiten hinzufügen, aber genau wie beim Erben von einer Superklasse hast du auch Eigenschaften dabei (siehe alle Eigenschaften die du im Inspektor in deinen Komponenten siehst). Komponentenbasiertes Design hat Vorteile gegenüber Vererbung und Interfaces, die gerade bei der Spieleentwicklung extrem auffallen. Wenn du also mit GameObjects arbeitest, die bereits verstehen, wie Komponenten funktionieren, dann empfehle ich ganz stark, dich da reinzufuchsen, wie man Komponenten benutzt um völlig auf Vererbung und Interfaces zu verzichten. Habe dazu mal einen Blogartikel geschrieben. Meine Komponenten haben inzwischen eigentlich fast niemals mehr Vererbung oder Interfaces.

Wenn du allerdings gerade nicht mit GameObjects arbeitest, dann könnte man komponentenbasiertes Design bei geringer Komplexität schon als Over-Engineering bezeichnen. Ist jetzt auch kein Bachelorarbeitsthema, ein kleines Komponentensystem zu basteln, aber mit Vererbung und Interfaces kommt man schon sehr weit.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 1 month later...

Hallo Sascha,

ich wollte schon länger antworten. Da hätte ich aber zugeben müssen das ich das komponentenbasiert  Desing, welches du in deinem Blog erklärt hast, nicht verstanden habe. :D Und ich keinen Sinn darin sehe, will damit nicht sagen das es kein Sinn hätte, sondern ich ihn nicht erkenne. So wie ich jahrelang den Sinn von Interfaces nicht erkannte. Dein letzter Satz gab mir auch richtig Auftrieb. Mittlerweile haben mir die Interfaces meine ganzen Enums ersetzt. Für jeden Struct/class hatte ich einen extra Enum. Was mich immer wieder lähmte weil es schon so überladen aussah, bevor man überhaupt anfing. Dieses Blanko Design finde ich großartig....schließlich haben wir ausnahmslos alle in der Schule bei jedem neuen Thema auch ein neues Blatt genommen. :) Weiß nicht ob du das verstanden hast, ob ich nicht wieder irgendwo einige Gedanken verkürzt habe. Jedenfalls bin ich programmiertechnisch da , wo ich immer sein wollte. Mir völlig egal ob das  4 Jahre  gedauert hat. :)

Meine Emailadresse müsste ich ändern, finde im Profil nicht wo ich das machen könnte. Die jetzige gibt es nicht mehr.

 

Schöne Ostern Sascha! 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mehr Werkzeuge zu kennen ist immer gut, egal, ob ich die jetzt spezifisch empfehlen würde :)

Interfaces sind leider etwas problematisch, wenn man sie in UnityEngine.Objects (und dazu gehören MonoBehaviours) implementiert. Mal davon abgesehen, dass komponentenbasiertes Design schlichtweg flexibler ist als OOP, welches dich in bestimmten Situationen von Natur aus so einschränken kann, dass du keine ganz saubere Lösung damit bauen kannst. Fällt dann besonders bei Spieleentwicklung irgendwann auf. Ist also gut, dass da jetzt ein Verständnis da ist, aber setze dich noch irgendwann einmal an Komponenten ran. Wenn das dann erstmal sitzt, ist das eine Erleuchtung sondergleichen. Lohnt sich dann auch, wenn das nochmal vier Jahre dauern sollte ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...