1

This is a subsequent question to another question I asked here: Calling constructor from other constructor in same class at the end

The previous one was about how, now the question is why Microsoft designed it this way ?

UPDATE: My question is rather:

Why can't I call a constructor directly AT THE END of another constructor whereas I can call AT THE BEGINNING.

If they forbid call at the end why didn't they forbid to call directly at the beginning also ?

Community
  • 1
  • 1
programmernovice
  • 3,841
  • 11
  • 43
  • 63
  • 11
    How about giving those that answered your last post referenced here some street cred and mark your previous question as answered :) – Jayden Sep 16 '09 at 07:21
  • 1
    Can you ask a more specific question? "Why was it designed this way?" is a very difficult question to answer meaningfully because the answer is always the same: "because that seemed like the best way to do it". Design is always a process of compromise in the face of dozens of mutually competing principles that have to be balanced against each other. There's not going to be a simple, crisp answer to any "why was this designed like this?" question -- a proper answer involves a deep and lengthy discussion of language design philosophy that I'm not prepared to give this early in the morning. – Eric Lippert Sep 16 '09 at 14:56
  • @Jayden sure, I just need to re-read to choose which one I will mark as best answer :) – programmernovice Sep 16 '09 at 18:31
  • @Eric every design is a compromise, but what are they ? Saying it's the best is an easy answer you can always give even for a bad design. Bad design exists or programming language wouldn't have evolved. – programmernovice Sep 16 '09 at 18:33
  • I think the real question is ONE-PLACE or WHEREVER-I-WANT. If you were to offer BEGINNING or END, then someone is then going to ask: why did someone forbid me from calling constructors where I want? If you go for ONE-DEFINED-PLACE it *has* to be the beginning - because otherwise inheritance is going to be a nightmare and you need to call base. If you want to do it anywhere, the compiler will probably have to start treating the constructor like a regular function and there'd be some trade off which powers-greater-than-I could probably relate (Eric I'm looking at your blog). – Joel Goodwin Sep 16 '09 at 19:41
  • Well, it's straightforward. We don't need to have a way to treat ctors as regular methods because we already have regular methods; we're giving you a tool to solve the problem, so use it. – Eric Lippert Sep 16 '09 at 19:54

7 Answers7

7

Why can't I call a constructor directly AT THE END of another constructor whereas I can call AT THE BEGINNING.

Well, let's break it down into two cases. (1) you're calling a "this" constructor, and (2) you're calling a "base" constructor.

Suppose you're in the first case. The typical usage pattern for this scenario is to have a bunch of ctors that take different arguments and then all "feed" into one master ctor (often private) that does all the real work. Typically the public ctors have no bodies of their own, so there's no difference between calling the other ctor "before" or "after" the empty block.

Suppose you're in the first case and you are doing work in each ctor, and you want to call other ctors at times other than the start of the current ctor.

In that scenario you can easily accomplish this by extracting the work done by the different ctors into methods, and then calling the methods in the ctors in whatever order you like. That is superior to inventing a syntax that allows you to call other ctors at arbitrary locations. There are a number of design principles that support this decision. Two are:

1) Having two ways to do the same thing creates confusion; it adds mental cost. We often have two ways of doing the same thing in C#, but in those situations we want the situation to "pay for itself" by having the two different ways of doing the thing each be compelling, interesting and powerful features that have clear pros and cons. (For example, "query comprehensions" vs "fluent queries" are two very different-looking ways of building a query.) Having a way to call a ctor the way you'd call any other method seems like having two ways of doing something -- calling an initialization method -- but without a compelling or interesting "payoff".

2) We'd have to add new language syntax to do it. New syntax comes at a very high cost; it's got to be designed, implemented, tested, documented -- those are our costs. But it comes at a higher cost to YOU because you have to learn what the syntax means, otherwise you cannot read or maintain other people's code. That's another cost; again, we only take the huge expense of adding syntax if we feel that there is a clear, compelling, large benefit for our customers. I don't see a huge benefit here.

In short, achieving your desired construction control flow is easy to do without adding the feature, and there's no compelling benefit to adding the feature. No new interesting representational power is added to the language.

For the "base" scenario, it's quite straightforward. You never want to call a base constructor AFTER a derived constructor. That's an inversion of the normal dependency rules. Derived code should be able to depend on the base constructor having set up the "base" state of the object; the base ctor should never depend on the derived constructor having set up the derived state.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I like this kind of mathematical proof will have to re-read it this we for my head is too full with my job at work this week :) – programmernovice Sep 17 '09 at 19:45
  • Also, with the new C# 4.0 default parameters feature, we'll see a decreasing need for the Eric's first case of calling the "this" constructor. – Rafa Castaneda Mar 30 '10 at 20:01
4

Eric Lippert has a blog post about the order in which initializers and constructors run when an object is created. In it he says: "The more derived constructors may rely upon state initialized by the less derived constructors, so the constructors should run in order from base to derived".

heijp06
  • 11,558
  • 1
  • 40
  • 60
3

I think the technical reason for the behaviour you describe is hinted within the C# specification here:

http://en.csharp-online.net/ECMA-334:_17.10.3_Constructor_execution

Any variable initializers are called as part of the constructor code, and if you were able to call a constructor directly - which is a Very Special Function from the compiler's point of view - you might fire off additional behaviour. With the constructor chaining approach (using the ": this" on the constructor definition) the compiler respects your attempt to call another constructor safely.

I don't think there's really much requirement to call another constructor within the same class (inheritance aside) -- there's nothing you can't do without defining another function and, to be honest, I prefer to do that. If my constructors get lengthy, I usually feel more comfortable pushing it into separate function as I often need to call a complicated "Reset" or "Init" function outside of the constructor.

Joel Goodwin
  • 5,026
  • 27
  • 30
  • "there's nothing you can't do without defining another function", well there's nothing you can't do with procedural programming either :) In fact when I ask why can't I call the constructor directly, I forgot to refine and rather ask: Why can't I call a constructor directly at the end of another constructor whereas I can call at the beginning. – programmernovice Sep 16 '09 at 18:39
2

IIRC from the design of C++, constructors must be called in top-down order because a base class instance is not guaranteed to be useable until its constructor has finished executing. You cannot construct your derived object safely unless you know in advance that your base object is fully initialised and ready to go. If that seems like a trivial point, consider the case where the base class contains private state that is only initialised properly in its constructor. AFAIK the reasoning is the same in the CLR.

The same reasoning applies in the opposite direction. It is only safe to destroy/finalize the base class instance after the derived class instance has been done, because the derived object may rely on the base object's state still being valid.

Christian Hayter
  • 30,581
  • 6
  • 72
  • 99
1

I'm guessing, but possibly as simple as: it is very rare you want it, and when you do there are usually better options (private/protected initialization methods) that do the job adequately. And without the risk of forgetting to initialize it, or calling a method that relies on data that isn't yet initialized (a reason why calling virtual methods in the ctor is a bit risky).

The only caveat is that you can't use a readonly field with a post-ctor initialization method, but that is IMO less evil than making things more complex for a corner-case.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • It may be very rare but it the rare even that makes a project slip from its track. Could you demonstrate that when you need it, there are better options. As I said in update, it is in fact possible to call constructor directly but only at the beginning not at the end. So is it really because they didn't bother to implement a rare feature that would be usefull or is it because it is bad design ? – programmernovice Sep 16 '09 at 18:44
1

The purpose of a constructor is to initialize the object. Overloading the constructors is used when you want to pass some initial parameters to the object.

So the purpose of the design is to have one Mega constructor which accepts all the variables and do all the initializing, and have the other constructors propagate variables, and set default values to parameters not passed from the user.

The idea is to have a single method doing the initialization, and not having seperate constructors each doing something else.

Amirshk
  • 8,170
  • 2
  • 35
  • 64
1

The default constructor is only ever created by the compiler when you don't specify your own constructor. It is a bit of syntactic sugar, to make it easy to quickly create a class.

By writing your own constructor, you are in effect declaring that yes, you are interested in how your objects are initialized. The default constructor is not generated.

Since disabling the default constructor is only possible by writing another constructor, allowing users to call the default constructor would make it impossible to disable it.

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200