Ich versuche, generische Algorithmen in C # zu schreiben, die mit geometrischen Objekten unterschiedlicher Dimension arbeiten können.
Im folgenden erfundenen Beispiel habe ich Point2
und Point3
beide implementieren eine einfache IPoint
Schnittstelle.
Jetzt habe ich eine Funktion GenericAlgorithm
, die eine Funktion aufruft GetDim
. Je nach Typ gibt es mehrere Definitionen dieser Funktion. Es gibt auch eine Fallback-Funktion, die für alles definiert ist, was implementiert wird IPoint
.
Ich habe anfangs erwartet, dass die Ausgabe des folgenden Programms 2, 3 ist. Es ist jedoch 0, 0.
interface IPoint {
public int NumDims { get; }
}
public struct Point2 : IPoint {
public int NumDims => 2;
}
public struct Point3 : IPoint {
public int NumDims => 3;
}
class Program
{
static int GetDim<T>(T point) where T: IPoint => 0;
static int GetDim(Point2 point) => point.NumDims;
static int GetDim(Point3 point) => point.NumDims;
static int GenericAlgorithm<T>(T point) where T : IPoint => GetDim(point);
static void Main(string[] args)
{
Point2 p2;
Point3 p3;
int d1 = GenericAlgorithm(p2);
int d2 = GenericAlgorithm(p3);
Console.WriteLine("{0:d}", d1); // returns 0 !!
Console.WriteLine("{0:d}", d2); // returns 0 !!
}
}
OK, aus irgendeinem Grund gehen die konkreten Typinformationen in verloren GenericAlgorithm
. Ich verstehe nicht ganz, warum das passiert, aber gut. Welche anderen Alternativen habe ich, wenn ich es nicht so machen kann?
NumDims
Eigenschaft verfügbar ist. Warum ignorierst du es in einigen Fällen?GetDim
(dh ich übergebe einePoint4
,GetDim<Point4>
aber nicht vorhanden). Es scheint jedoch nicht, dass der Compiler sich die Mühe macht, nach einer speziellen Implementierung zu suchen.Antworten:
Diese Methode:
... wird immer anrufen
GetDim<T>(T point)
. Die Überlastungsauflösung wird zur Kompilierungszeit durchgeführt , und zu diesem Zeitpunkt gibt es keine andere anwendbare Methode.Wenn Sie möchten, dass die Überlastungsauflösung zur Ausführungszeit aufgerufen wird , müssen Sie die dynamische Typisierung verwenden, z
Im Allgemeinen ist es jedoch besser, die Vererbung dafür zu verwenden. In Ihrem Beispiel könnten Sie natürlich nur die einzige Methode verwenden und zurückkehren
point.NumDims
. Ich gehe davon aus, dass es in Ihrem realen Code einen Grund gibt, warum das Äquivalent schwieriger zu tun ist, aber ohne mehr Kontext können wir nicht raten, wie die Vererbung zur Durchführung der Spezialisierung verwendet werden soll. Dies sind jedoch Ihre Optionen:quelle
AxisAlignedBoundingBox2
und habeAxisAlignedBoundingBox3
. Ich habe eineContains
statische Methode, mit der bestimmt wird, ob eine Sammlung von Boxen einLine2
oder enthältLine3
(welche davon vom Typ der Boxen abhängt). Die Algorithmuslogik zwischen den beiden Typen ist genau gleich, außer dass die Anzahl der Dimensionen unterschiedlich ist. Es gibt auch Anrufe zuIntersect
interne die auf den richtigen Typ spezialisiert werden müssen. Ich möchte virtuelle Funktionsaufrufe / Dynamics vermeiden, weshalb ich Generika verwende. Natürlich kann ich den Code einfach kopieren / einfügen und weitermachen.Ab C # 8.0 sollten Sie in der Lage sein, eine Standardimplementierung für Ihre Schnittstelle bereitzustellen, anstatt die generische Methode zu benötigen.
Das Implementieren einer generischen Methode und Überladungen pro
IPoint
Implementierung verstößt auch gegen das Liskov-Substitutionsprinzip (das L in SOLID). Es ist besser, den Algorithmus in jedeIPoint
Implementierung zu übertragen, was bedeutet, dass Sie nur einen einzigen Methodenaufruf benötigen sollten:quelle
Besuchermuster
Als Alternative zur
dynamic
Verwendung möchten Sie möglicherweise ein Besuchermuster wie folgt verwenden :quelle
Warum definieren Sie die GetDim-Funktion nicht in Klasse und Schnittstelle?? Eigentlich müssen Sie die GetDim-Funktion nicht definieren, sondern verwenden Sie einfach die Eigenschaft NumDims.
quelle