Klingt für mich, als ob Sie nach template this parameters suchen.
class Base { T[] sequence(this T)() { return null; } }
class Derived : Base {}
static assert(is(typeof((new Derived).sequence()) == Derived[]));
Beachten Sie jedoch, dass sequence
oben ist keine virtuelle Funktion, und auch, dass die Vorlage im Rahmen der Basisklasse instanziiert wird. Um dem Benutzer eine einfache Schnittstelle zur Verfügung zu stellen, möchten Sie wahrscheinlich eine spezialisierte Funktion verwenden. Dies kann entweder eine virtuelle Funktion sein, oder eine Ente typisierte Funktion in der Unterklasse (in diesem Fall können Sie den Rückgabewert der Funktion müssen cast):
class Base {
T[] sequence(this T)() {
// Virtual call, requires unsafe cast of return type.
return cast(T[])sequenceImplVirtual();
// Non-virtual call, requires safe cast of this reference
// and will fail if the subclass doesn't implement it correctly.
return (cast(T)this).sequenceImplDuck();
}
abstract Base[] sequenceImplVirtual();
}
class Derived : Base {
Derived[] sequenceImplDuck() {
return [this];
}
override Base[] sequenceImplVirtual() {
return [this];
}
}
unittest {
Derived[] arr = (new Derived).sequence;
}
Der virtuelle Anruf könnte verlockendsten erscheinen, als Es gibt einen Kompilierungsfehler, wenn die Unterklasse sequenceImplVirtual
nicht implementieren kann. Beachten Sie jedoch, dass die überschreibende Funktion nicht den Anspruch erhebt, eine Derived[]
zurückzugeben, und wenn Sie versehentlich eine Base
oder eine andere Klasse zurückgeben, die nicht von Derived
abgeleitet ist, wird das Programm segfault. Die explizite Besetzung verbirgt dies effektiv. Eine etwas ausführlichere Programm könnte dies testen:
T[] sequence(this T)() {
import std.algorithm.searching : all;
auto result = sequenceImplVirtual();
assert(result.all!((Base a) => a is null || cast(T)a !is null));
return cast(T[])result;
}
Dies gibt eine einfach zu versteh zur Laufzeit geltend machen Fehler, wenn sequenceImplVirtual
einen ungültigen Wert zurückgibt.
Die Duck-typed-Lösung andererseits gibt keinen Hinweis, dass Sie vergessen haben, sequenceImplDuck
zu implementieren, bis Sie es verwenden. Doch aufgrund es nur eine sichere Besetzung (cast(T)this
), der Compiler garantiert dabei, dass der Rückgabewert ist in der Tat ein Derived[]
:
class Base {
T[] sequence(this T)() {
return (cast(T)this).sequenceImplDuck();
}
}
class Derived : Base {
// Note: wrong return type.
// Will fail to compile when you call sequence().
Base[] sequenceImplDuck() {
return [this];
}
}
class Derived2 : Base {
// Note: No implementation.
// Will fail to compile when you call sequence().
}
unittest {
Derived[] arr = (new Derived).sequence;
auto d = new Derived2;
auto arr2 = d.sequence;
}
Die oben wird fehlschlagen, wenn sie mit -unittest
kompiliert, aber wenn Sie den Unittest auf Kommentar, oder kompilieren Sie ohne -unittest
, der Compiler gibt keine Angabe, dass Derived
oder Derived2
die erforderliche Funktion nicht ordnungsgemäß implementieren, die der virtuelle Aufruf wird.
Wenn Sie mehrere untergeordnete Klassen haben und eine Liste, die alle mit dem Typ der Basisklasse enthält, was würden Sie erwarten, dass 'sequence' für jedes Element zurückgegeben wird? Ein anderer Typ für jeden? So funktioniert statische Typisierung nicht. Obwohl Sie in der Lage sein können, was Sie wollen mit Standard-Polymorphie bedeutet. Was möchten Sie eigentlich tun? – weltensturm
Nun, das ist nicht genau das, was ich tun möchte, aber um der Erklärung willen funktioniert es: Angenommen, ich möchte eine abstrakte Klasse wie SerializedElement, von der XMLElement und JSONElement erbt. Ich möchte, dass jede untergeordnete Klasse über eine Methode/Eigenschaft verfügt, um die untergeordneten Elemente zurückzugeben, die offensichtlich derselben Klasse angehören. Wie würde ich (wenn das überhaupt möglich ist) eine abstrakte Methode in SerializedElement implementieren, die ein Array von welchem Typ auch immer das Kind ist (XMLElement oder JSONElement) zurückgibt? –