Problem mit Generics
-
Hi Leute,
habe hier folgendes Problem:
Eine Basisklasse BaseA, davon zwei abgeleitete Klassen DerivedAA und DerivedAB, wobei DerivedAA Elemente vom Typ DerivedAB enthält und DerivedAB ein Element vom Typ DerivedAA:
abstract class BaseA { } class DerivedAA : BaseA { List<DerivedAB> Children; } class DerivedAB : BaseA { DerivedAA Parent; }
Dann gibt es analog dazu eine Basisklasse BaseB und zwei abgeleitete Klassen DerivedBA und DerivedBB. Diese wiederum verwenden die Klassen DerivedAA und Derived AB:
abstract class BaseB { abstract double GetTotalValue(DerivedAA aa); abstract double GetTotalValue(DerivedAB ab); } class DerivedBA : BaseB { List<DerivedBB> Children; override double GetTotalValue(DerivedAA aa) { double totalValue=0; foreach(DerivedBB bb in Children) totalValue+=bb.GetTotalValue(aa); return totalValue; } override double GetTotalValue(DerivedAB ab) { double totalValue=0; foreach(DerivedBB bb in Children) totalValue+=bb.GetTotalValue(ab); return totalValue; } } class DerivedBB : BaseB { DerivedBA Parent; Dictionary<DerivedAB, double> values; override double GetTotalValue(DerivedAA aa) { double totalValue=0; foreach(DerivedAB ab in aa.Children) totalValue+=bb.GetTotalValue(ab); return totalValue; } override double GetTotalValue(DerivedAB ab) { return values[ab]; } }
Es soll also mit jeder beliebigen Konstellation von DerivedAA, DerivedAB, DerivedBA und DerivedBB möglich sein, den Gesamtwert dieser Konstellation herauszubekommen.
Dazu gibt es eine generische Methode
double GenericGetTotalValue<A, B>(List<A> as, List<B> bs) where A : BaseA where B : BaseB { double totalValue = 0; foreach(A a in as) { foreach(B b in bs) { totalValue += b.GetTotalValue(a); // <-- } } return totalValue; }
Das geht aber so nicht, Kompilerfehler:
Argument '1': Cannot convert from 'A' to 'DerivedAA'.Wie mach ich das denn am Besten?
(Der Code ist als Pseudocode zu verstehen)
-
Nach DerivedAA casten ?!
-
oder in der GetTotalValue nur ein Base verwenden und dies innerhalb der Mehtode casten / sicherstellen das es der richtige Typ ist.
-
So ein Unsinn
Hat jemand noch eine gescheite Variante?
-
Wieso Unsinn ?!
Du hast den Basistypen und der Compiler soll das für Dich automatisch in einen abgeleiteten Typen umwandeln ?
Woher soll der Compiler denn wissen welchen abgeleiteten Typ er verwenden soll ?Schau Dir doch die Signatur mal genau an und schau Dir an, was a für ein Typ ist an der Stelle.
Das zweite Problem ist, das Dein a von einer Foreach kommt und nicht veränderbar ist. Zum casten musst Du daher
eine neue Referenz anlegen.public double GenericGetTotalValue<A, B>(List<A> as2, List<B> bs) where A : BaseA where B : BaseB { double totalValue = 0; foreach(A a in as2) { foreach(B b in bs) { BaseA x = a as DerivedAA; totalValue += b.GetTotalValue((DerivedAA)x); // <-- } } return totalValue; }
Vllt. klärst Du mich aber einfach mal auf, woher der Compiler die Information nehmen soll, wohin er Deinen Typ casten soll. Es wirkt alleine schon merkwürdig, das der Basistyp, von dem viele andere Typen abgeleitet werden dürfen, in den GetTotalValue als Parameter jeweils einen erbenden Typ annehmen.
Es mag sein das ich über die Jahre nix gelernt hab, aber mir erscheint die Erwartung die Du hast unsinnig zu sein. Du hast den Basistyp und der Compiler erwartet bei Deiner Methode einen konkreten Typ.
-
Das ganze Model ist darauf ausgelegt um eben if-Abfragen zu vermeiden. Das ist die Hauptsache. Dass es mit diesen überladenen Methoden nicht geht ist ja inzwischen klar. Wieso es nciht geht bei näherem Nachdenken auch. So, das ändert aber eben nix daran, dass ich hier ohne if-Abfragen auskommen will weil ich dieses Konstrukt ziemlich häufig habe... Man muss also möglicherweise einen komplett anderen Ansatz wählen...
-
Du willst multiple dispatch, C# bietet aber nur einfaches.
Ohne zu redisignen bekommst du es so hin
totalValue += b.GetType().InvokeMember("GetTotalValue", System.Reflection.BindingFlags.InvokeMethod, null, b, new object[] { x });
Ansonsten Typecaseing oder was Visitor-artiges.