简体   繁体   中英

C#: Why can't I pass 'this' as a constructor argument to a base class?

You can't pass this as an argument to a base constructor - see eg, C# language specification section 10.10.1 Constructor initializers (last line on page).

I don't understand this limitation and would like to. In C#, as opposed to C++, the instance being constructed is already at its actual type and "everything works" (though of course not everything is initialized; I mean that virtual functions called in a derived class' constructor execute the derived class' methods). Base classes are allowed to call virtual methods on themselves even though the derived class' override will be executed and the derived class might not be ready for it; that's not ruled out. So what is the reason for this restriction?

(In C++ this is allowed for three reasons. First, the user or a derived class is supposed to know what he is doing. Second, the user of C++ is supposed to know what he is doing. And third, even if the user doesn't know what he is doing the C++ philosophy is to give him the rope he requires to hang himself, and then facilitate him when he ties the knot. I actually like that philosophy!)

What I'm trying to do, by the way, is construct properly initialized list members in a circularly-linked list. There's a base class Element with a field Link to point to the next element. There's a class Head which derives from Element , it'll be the distinguished sentinel for the start of the list plus have special behavior for the entire list. The constructor for Element takes the head of the list as argument, I want to write as follows to initialize elements and the head properly:

class Element {
    protected Element link;
    public Element(Element prior) 
    {
        this.link = null;
        prior.link = this;
    }
};

class Head : Element {
    public Head() : base(this) {}
};

(Before you complain that I should do this example differently my actual code is somewhat more complicated - a specialized sparse array - and I have my reasons.) I'm going to use a factory for Lists (and a factory method on Head for adding elements) to avoid this, which arguably is better design, but I'm puzzled that this other reasonable method is outlawed.

The design of C# and .NET is intended to give the base class control over its invariants. Once the base-class constructor has been completed, derived-class code, including derived-class constructors, is allowed to manipulate the base-class portion of the object in any way allowed by the base class. A derived class cannot do anything with the object under construction other than storing values to its own (derived-type) fields prior to the base-class constructor call, but a base class contract can in effect say "I'm going to call this virtual method during my constructor, and any legitimate derived class must be prepared to deal with that".

The design does pose some limitations. The worst IMHO is that there is no mechanism by which a base class can assert control of the construction process at any time after the derived-class construction code gets access to its constructor parameters.

C# evaluates field initialization expressions before chaining to the base constructor, on the theory that this will allow derived-class objects to set themselves up before the base constructor calls their virtual methods; a consequence of this is that field-initializer expressions can't do anything with the object under construction. VB.NET runs initializers after the base constructor, which allows more convenient access but means fields will be unitialized if the base constructor calls any virtual methods. Personally, I would regard the C# approach as "almost" useful, but the inability to handle classes whose invariants would be affected by constructor parameters severely limits its usefulness.

What I'd really like to see, btw, would be for Object to include a virtual method that will run between the time the most derived constructor finishes execution or throws an exception and the time control returns to the code that calls foo = new Bar(); . The primary reason that base-class constructors expose the objects under construction before derived-class constructors get to run is that there is no standard way for them to ensure that they'll ever get control after that. Such a design would greatly improve constructor sequencing. I'm not holding my breath for such a thing, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM