Kurumi-chan Geschrieben 29. März 2023 Melden Share Geschrieben 29. März 2023 Hey Leute, ich würde gerne wissen, ob es möglich ist einen Parameter einer generischen Klassen dynamisch zu erstellen? Als Beispiel: internal class DatabaseEditorSubWindow<DB> : IDatabase where DB : ScriptableObject {} //Wird aufgerufen, wenn ein bestimmter Button geklickt wird. Der Typ der Datenbank wird mitgegeben Hier wird nun geschaut, welche Daten die Datenbank enthält. Dies können z.B. Listen verschiedenen Typs sein. Diese werden geladen und deren Inhalte in eine Variable gepackt. Anschließend werden Buttons generiert, mit denen die Einzelnen Inhalte geladen werden sollen. Dies geschieht dann über eine neue Klasse. internal class DatabaseCoreWindow<DB, TType> : IDatabase where DB : ScriptableObject where TType ScriptableObject, IName {} // Zeigt die Daten an, erstellt neue Inhalte, usw.. Der Aufruf der Klasse sieht folgendermaßen aus m_Child = new DatabaseCoreWindow<DB, (Type)toolbarTypes[i]>(m_Database, toolbarList); // Hier soll TType dynamisch gesetzt und durch den Typ der Liste ersetzt werden, z.B. Item, oder Skill Ich habe dazu verschiedenste Ansätze versucht, aber nichts hat funktioniert. Entweder es kommen Fehler, oder es werden die mitgegebenen Parameter nicht gesetzt. Hat evtl. jemand eine Idee, wie ich das umsetzten kann? Vielen Dank für die Hilfe. LG Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 29. März 2023 Melden Share Geschrieben 29. März 2023 Moin! Ich bin gerade nicht so ganz fit, daher verstehe ich die Frage vielleicht nur so halb. Aber wenn ich die Hälfte immerhin richtig verstehe, dann geht es darum, ein System.Type zu haben und diesen als generischen Parameter zu nehmen. Das ist nicht vorgesehen, da Generics ein "statisches" Konzept ist, das zur Compile Time feststeht und dadurch mehr Sicherheit gibt als dynamisches Umherwerfen von Typen zur Laufzeit. Der korrekte Weg ist daher eine Überladung der Methode mit einem normalen Parameter vom Typ System.Type. Macht GetComponent auch so (wobei "Unity macht das auch so" natürlich kein starkes Argument ist ). Alternativ geht das theoretisch auch mit Reflection. Man kann da aber den generischen Typ (typeof(DatabaseCodeWindow)) nehmen und den mit MakeGenericType() inflaten (also generische Parameter setzen). Diesen Typ kannst du dann in den Activator stecken, um eine Instanz zu erzeugen. Reflection ist aber nur in einigen wenigen Sonderfällen eine gute Idee, z.B. wenn dein Code mit anderem Code funktionieren soll, den du jetzt noch nicht kennst, z.B. weil du ein Paket schreibst, das andere dann weiter verwenden. Wenn das nicht der Fall ist, solltest du Reflection eher meiden. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Kurumi-chan Geschrieben 29. März 2023 Autor Melden Share Geschrieben 29. März 2023 Hey Sascha, danke dir für deine Antwort. Ja, habe schon befürchtet, bzw. gelesen und gesehen, dass es so nicht funktioniert, wie ich es mir wünsche. Aber hatte die Hoffnung, dass wenn ich direkt jemanden danach Frage, derjenige evtl. doch einen Tipp, oder Idee hat. Schade, dann werde ich wohl doch mit switches arbeiten müssen. Wäre anders ja auch zu einfach gewesen^^ Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 29. März 2023 Melden Share Geschrieben 29. März 2023 vor 1 Stunde schrieb Kurumi-chan: Schade, dann werde ich wohl doch mit switches arbeiten müssen. Hmm... müssen tut man das nie. Ich bin mir gerade nicht ganz sicher, aber mir fällt gerade kein Fall ein, wo man etwas mit einer generischen Methode machen kann, was man nicht auch mit einem System-Type-Parameter machen könnte. Außer Boxing bei Structs vermeiden... Was machst du denn am Ende mit deinem generischen Typparameter, dass du nicht auch ein System.Type-Objekt dafür nehmen kannst? Und so nebenbei: switch-case kannst du üblicherweise mit einem Dictionary vermeiden. Sollte man machen, wenn das switch-case potentiell immer weiter wachsen würde. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Kurumi-chan Geschrieben 30. März 2023 Autor Melden Share Geschrieben 30. März 2023 Der Parameter gibt den Typ des Datanbankobjekts an, also z.B. Item, Skill, Slot, usw.. Und davon wird dann jeweils die Liste an verfügbaren Objekten geladen und sobald ich eines aus der Liste anklicke, wird der entsprechende Inspector aufgerufen. Nur muss dafür ja der genaue Type bekannt sein, sonst bekomme ich die Daten ja nicht? Die Datenbank ist so aufgebaut, das im Db Objekt die verschiedenen Datenbanken liegen, also z.B. ItemDatabase, SettingDatabase, usw.. Und darin sind dann mehrere Listen. Im Fall ItemDatabase wären das Item, Slot, Currency. Die einzelnen Listen generieren zum einen einen neuen Menüpunkt und sobald man einen davon klickt, wird die entsprechende Liste geladen und angezeigt, sowie der erste Eintrag der Liste geladen. Wenn man nun in der Liste einen anderen eintrag wählt, wird halt dieser geladen, usw. Ps: Das Bild nicht auf die Goldwaage legen, das ist alles noch nicht fertig. 😄 Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Kurumi-chan Geschrieben 30. März 2023 Autor Melden Share Geschrieben 30. März 2023 vor 22 Stunden schrieb Sascha: Und so nebenbei: switch-case kannst du üblicherweise mit einem Dictionary vermeiden. Sollte man machen, wenn das switch-case potentiell immer weiter wachsen würde. Wäre das nicht das selbe nur in Grün? Ich müsste ja auch dort den Datentypen angeben. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 30. März 2023 Melden Share Geschrieben 30. März 2023 vor 21 Minuten schrieb Kurumi-chan: Nur muss dafür ja der genaue Type bekannt sein, sonst bekomme ich die Daten ja nicht? Korrekt! Aber du kannst Typen eben nicht nur über generische Parameter kommunizieren, sondern auch über einen normalen System.Type-Parameter. Statt public void Foo<T>() machst du public void Foo(System.Type type) Du kannst generische Parameter auch problemlos mit typeof() zu einem System.Type-Objekt umwandeln. So als Beispiel: public bool Foo<T>(System.Type type) { return typeof(t) == type; } Das gibt dann true zurück, wenn man das so aufruft: Foo<Light>(typeof(Light)) // oder Foo<Light>(GetComponent<Light>().GetType()) Umgekehrt braucht man halt leider Reflection und sollte das vermeiden. Aber man kann oft von der anderen Seite ankommen, z.B. so: class MyGenericClass<T> { public System.Type GenericType => typeof(T); } Objekte dieser Klasse geben die mit der Property "GenericType" den Typ zurück, den du beim Erstellen für T übergeben hast: var thing = new MyGenericClass<Light>(); Debug.Log(thing.GenericType.Name); // "Light" Damit geht eine ganze Menge. Du könntest zum Beispiel beim Start alle deine Objekte laden und sie in ein Dictionary packen. Das Dictionary kann so aussehen: private Dictionary<System.Type, List<MyGenericClass<>> genericClassObjectsByGenericType = new Dictionary<...>(); Und dann lädst du da alle geladenen Objekte rein und sortierst sie nach ihrem generischen Typ: foreach (var loadedObject in allLoadedObjects) { var type = loadedObject.GenericType; List<MyGenericClass<>> list; if (genericClassObjectByType.TryGetValue(type, out list)) { list.Add(loadedObject); } else { list = new List<MyGenericClass<>>(); list.Add(loadedObject); genericClassObjectByType.Add(type, list); } } Und wenn du dann alle Objekte eines bestimmten generischen Type haben willst, holst du sie dir einfach aus dem Dictionary: public List<MyGenericClass<>> GetAllObjectsForType(System.Type type) { return genericClassObjectsByType[type]; } Da fehlt noch die Ausnahmebehandlung, falls der Typ nicht gefunden wird. Und ich hab das jetzt so runtergetippt, kann sein dass die Syntax mit <> nicht ganz richtig verwendet wird. Aber ich hoffe, die Idee ist einigermaßen klar. vor 1 Minute schrieb Kurumi-chan: Wäre das nicht selbe nur in Grün? Ich müsste ja auch dort den Datentypen angeben. Ja schon, aber eine Datenstruktur wachsen zu lassen ist besserer Stil als ein switch-case, das ständig erweitert werden muss. Vor allem, weil du bei einem Dictionary einfach mit der Schleife über alle Typen gehen kannst (siehe oben), anstatt deinen switch-case-Code jedes Mal anzufassen. 1 Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Kurumi-chan Geschrieben 30. März 2023 Autor Melden Share Geschrieben 30. März 2023 Oha, wow. Sascha, ich danke dir vielmals für diesen hilfreichen Ansatz. Das werde ich morgen mal in Angriff nehmen. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.