2017-10-19 1 views
0

In D ist es möglich, innerhalb einer Elternklasse auf eine untergeordnete (erbende) Klasse zu verweisen?Vorlage, die sich auf die Kindklasse innerhalb der Elternklasse bezieht

habe ich versucht, so etwas wie diese:

abstract public @property typeof(this)[] sequence(); 

die Hoffnung, dass typeof(this) das Kind Klasse lösen würde, der diese Methode anstelle der übergeordneten Klasse überschreibt; aber das tut es nicht. Gibt es eine Möglichkeit, das zu tun?

Vielen Dank im Voraus.

+0

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

+0

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? –

Antwort

3

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.

Verwandte Themen