2015-09-29 12 views
5

Ich entdeckte etwas seltsames, was ich nicht erwartet zu arbeiten, tatsächlich funktioniert. Ich kann eine Unterklasse (konstant) Eigenschaft von der Basisklasse Konstruktor zugreifen:Access Unterklasse 'Eigenschaft von der Basisklasse' Konstruktor

public abstract class Parent { 

    public Parent() { 
    var constName = ConstName;   // <-- surprisingly, this works 
    var randomName = RandomName;   // <-- surprisingly, this works 
    } 

    public abstract string ConstName { get; } 

    public abstract string RandomName { get; } 

} 


public class Child : Parent { 

    public override string ConstName { get { return "Mike"; } } 

    public override string RandomName { get { return new Random().Next().ToString(); } } 

} 

Name ist eine nicht-statische Eigenschaft statt Feld. Ich dachte immer, dass die Initialisierer eines Typs (für und const Felder) ausgeführt wurden, dann die seiner Basisklasse, dann die Basis-Ctor und dann die Unterklasse "Ctor. Das bedeutet, dass das Kind noch nicht vollständig konstruiert ist, während es im Elternteil ist.

Ist diese "legale" C# unter allen Umständen funktionieren? Warum funktioniert das?

EDIT:

Nein, es ist kein Betrogene Frage. Dieser hat nicht das Klassenschema in meiner Frage.

+0

Sie haben Name Eigentum in Parent! Das ist technisch gesehen legal und korrekt. –

+0

@Nikita Ja, aber Kind ist noch nicht konstruiert. Lesen Sie die Reihenfolge der Initialisierung in meiner Frage. –

+0

Sie sagen also, wenn Sie debuggen und es geht um Basiskonstruktion, Yu Name wie Mike? –

Antwort

3

Sie können auf die abstrakten/virtuellen Mitglieder der Subtypen zugreifen. Die Eigenschaftengetter sind Methoden am Ende.

Aber.

Wie Sie bereits gesagt haben, wurde der Unterklassenkonstruktor noch nicht ausgeführt. Daher kann es Nebenwirkungen haben. Deshalb erhalten Sie eine Warnung von FxCop.

public abstract class Parent 
{ 
    public Parent() 
    { 
    // NullReferenceException here: 
    var nameLength = Name.Length; 
    } 

    public abstract string Name { get; } 

} 

public class Child : Parent 
{ 
    private string name; 

    public Child() 
    { 
    name = "My Name"; 
    } 

    public override string Name { get { return name; } } 
} 

Noch schlimmer:

public class Child : Parent 
{ 
    private string name; 

    public Child() 
    { 
    name = "My Name"; 
    } 

    public override string Name 
    { 
    get 
    { 
     // NullReferenceException here. 
     // You didn't expect that this code is executed before 
     // the constructor was, did you? 
     return name.Substring(0, name.Length - 1); 
    } 
    } 
} 

Daher ist es nicht empfehlenswert zu tun.

+0

Rechts. Ich war also richtig überrascht von diesem Verhalten, weil es keinen Sinn ergibt. Ich nehme an, dass es nicht einfach war, dieses Verhalten zu verhindern, also hat das Compiler-Team es zugelassen, aber die FxCop-Warnung erstellt, um Ihnen zu sagen, dass es eine "sehr schlechte Idee" ist. Dies führt zu einer nicht-deterministischen Ausgabe! –

+0

Brilliant bearbeiten! Erklärt das Problem perfekt! +1 –

+0

Ich denke, dass meine Frage könnte so gedacht werden: nur weil etwas kompiliert, macht es nicht "richtig". Es gibt bizarre Randfälle, die der Compiler Ihnen nicht beibringen kann. Es gibt eine Menge dunkler Magie in Konstruktoren :). –

4

Parent hat eine abstrakte Eigenschaft Parent.Name. Aufgrund des Worts abstract versprechen Sie, dass Instanzen von (Unterklassen von) Parent die Eigenschaft Name implementieren. Wenn nicht, können Sie kein Objekt davon erstellen.

Beachten Sie, dass ich sage: Instanzen können nicht erstellt werden. Wenn eine Unterklasse die Eigenschaft Name nicht implementiert, kann die Klasse existieren, aber Sie können sie nicht instanziieren.

Klasse Parent implementiert keine Eigenschaft Name, daher können Sie Parent nicht instanziieren.

Klasse Child implementiert jedoch Name, daher können Sie es instanziieren, und weil Sie versprochen haben, dass jedes Objekt (= Instanziierung) der Klasse Parent eine Eigenschaft Name hat, können Sie sicher sein, dass Sie nur wissen, dass es ein Parent ist Du weißt auch, dass es einen Namen hat.

Dies ist das Grundprinzip des Polymorphismus in Subtypisierung Wikipedia about polymorphism

Es könnte der Hauptgrund, warum Sie zu Unterklasse wünschen würden.

+0

Ja, das macht Sinn. Sie beziehen sich auf einen Kompilierungsvertrag, damit Sie sicher sein können, dass die untergeordnete Eigenschaft vorhanden ist. Aber der Punkt hier ist, dass Sie es tun, bevor das Kind (vollständig) konstruiert ist. Das verstehe ich nicht. –

+0

Bei einem zweiten Lesevorgang erklärt Ihre Antwort grundsätzlich (sehr gut) die allgemeine Idee des Polymorphismus. Aber nicht die Reihenfolge der Konstruktion, die im Zentrum dieser Frage steht. –

+1

Aufgrund der abstrakten Funktion weiß der Konstruktor von Parent, dass das Objekt, das erstellt werden soll, eine Unterklasse von Parent ist. Obwohl die Konstruktion der Unterklasse noch nicht abgeschlossen ist, existiert die Unterklasse bereits vollständig. In jedem Konstruktor müssen Sie, selbst wenn es keine Basisklasse gibt, vorsichtig mit der Reihenfolge sein, in der Sie die Objekte des Objekts initialisieren. Sie können Methoden und Eigenschaften aufrufen, aber sicherstellen, dass sie nur Elemente verwenden, die bereits initialisiert sind. –

Verwandte Themen