Calling constructors in arbitrary places

Calling constructors in arbitrary places

Rate This
  • Comments 28

C# lets you call another constructor from a given constructor, but only before the body of the calling constructor runs:

public C(int x) : this(x, null)
{
  // …
}
public C(int x, string y)
{
  // …
}

Why can you call another constructor at the beginning of a constructor block, but not at the end of the block, or in the middle of the block?

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

For the "base" scenario, it's quite straightforward. You almost 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 constructor should never depend on the derived constructor having set up the derived state.

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

Suppose you're in the second case and you are doing work in each constructor, and you want to call other constructors at some point other than the start of the current constructor.

In that scenario you can easily accomplish this by extracting the work done by the different constructors into methods, and then calling the methods in the constructors in whatever order you like. That is superior to inventing a syntax that allows you to call other constructors 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 constructor 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 the 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.

  • Jonathan,

    I would ask why the derived constructor needs to do additional validation on parameters before passing them to the base class constructor.

    The base class should be validating it's own parameters and throwing on bad values. If it is not, then it is broken.

    If the derived class has additional restrictions then it should be acceptable to validate these after the base constructor has run and throw from there if the need arises.

    If that is not acceptable, for example: if the base constructor is time and/or resource intensive, then a factory method might be appropriate, although re-writing the base class to use lazier construction is probably a better idea in that case.

  • @Pavel Minaev

    You missed my point. Eric points out that introducing the new feature would imply a learning curve for the user because he would have to learn the new sintax in order to understand, debug or write new code. I dont agree, anyone who knows how to code in C# nowdays would understand base(...) or this(...) when encountered midway through a constructor body. So IMO eric's reasoning does not stand in this particular issue.

    I do mention however that the implementation of such feature is surely expensive and I am fully aware that it's not by any means trivial.

  • @Grico,

    The new syntax still carries a learning curve for the developer, because he has to learn at least some of the rules that would have to go along with it, as Pavel explained.

  • I don't know, I always thought it was for consistency:  base calls the base constructor *before* the current constructor; this calls another constructor *before* the current constructor.  It's always the other one *before* the current one, and this enforces a sort of logical consistency, as well as programmatic practicality (as this is exactly how I would expect both keywords to behave).

    The 'readonly' keyword I've always interpreted as a limited form of a public get/private set property, except you may only set it once, either in the initializer or in the current class constructor — though, I have not tested this, can you set a protected readonly field in a derived constructor?  Either way, readonly is simply a way to protect variables from being changed outside the current class while allowing them to be set in the constructor — in other words, a constant with limited mutability.  The fact we *can* assign a readonly variable in a constructor rather than *only* the initializer seems to me like a bonus.

    I know programmers in general are pretty lazy and expect a great deal more from their tools than the tool developers are willing or able to provide, but to me all this nitpicking about new features is rather silly because, as a programmer, half my job is figuring out how to work around tool and language limitations, and, call me crazy, but I consider that the *fun* part of my job.

    Not that I'm asking for more limitations, mind you.

  • >   Gabe said:

    I don't understand why new syntax would have to be created. They already created special syntax in the form of base() and this(), so why not just allow that in the body of a ctor?

    My impression is that calling a constructor is the same as creating a new instance, so if you call this.ctor within a constructor, you're effectively creating a new instance, and 'this' becomes meaningless because the constructor you just called no longer refers to 'this'.  It works the same with base:  you're creating a new base instance.

    Besides, modularizing functionality is kind of the whole point of object-oriented programming.  Why on earth would you cram all your functionality into a bunch of constructors when you can use methods?  Save the 'readonly' argument, but again, I consider that a privilege, not a right.  If you want a *readonly* value that can be assigned *outside* the constructor, it isn't readonly.  Your logic is broken.

    This was probably not the team's line of thinking when they implemented this limitation — or rather, failed to implement a workaround for this limitation — but I think it's a solid argument.

  • Tom,

    You mentioned: "...I have not tested this, can you set a protected readonly field in a derived constructor?"

    No it can not. The readonly field must be initialized in the base constructor or an initializer.

    Could someone post an example of how not being able to call a constrcutor in an arbitrary location affects readonly variables?

    I just can't see it.

  • Tom: The way I see it, the new operator creates an instance, and the ctor just initializes it. Remember, it's not functionality that's being crammed in, it's initialization code. If initialization isn't supposed to be a special operation, why are constructors so special in the first place?

  • Of course, someone pointed out off-thread out that instead of using a static factory, I could just have the public ctor call a private overload. So scratch my comment above.

  • @Mike

    > Could someone post an example of how not being able to call a constrcutor in an arbitrary location affects readonly variables?  I just can't see it.

    AFAICT, the issues here are readability and learning curves. Assuming you want to preserve the convention that Eric mentions, of base classes not depending on derived classes, you can do everything you want with the current tools; it's merely a question of how ugly (difficult to maintain or explain) you are willing make your code.

    Suppose you have a block B of code you want to execute inside your constructor, after another constructor in the same class.  Inside of invoking the second ctor partway through the first, you can extend your master constructor with enough additional parameters that it can conditionally execute block B, based on the parameters each "feeder" ctor sends.

    I'm not saying I'd recommend this.  As others have noted, when things get this ugly, it's often a sign that it's time to step back, reconsider, and refactor.

    Even the problem of readonly fields is tractable with current tools. Eric recommends "extracting the work done by the different constructors into methods, and then calling the methods in the constructors in whatever order you like."  If you're like me, you first interpreted this to mean that each block of code would be moved wholesale, into a method that encapsulates the entire block--but there's a middle ground.  You can extract methods that do the "work" of calculations, decisions, etc., but that do not do the final step of assigning state to fields.  Instead, you can return values from these methods and perform the readonly assignments back in the ctors.  Ta da--DRY methods and readonly access.

    Again, I'm not saying I'd enjoy this style, either.  But, there's nothing mentioned so far that's impossible with C# 3.0, from a mechanical perspective.

  • Mark,

    The whole notion of doing the work in methods and returning values to be assigned to readonly fields seemed obvious to me. I guess that's why I don't see any problem. I certainly would consider a base class depending on a derived implementation to be a serious problem and refactor it immediately.

    I think some limitations on the way we code are actually helpful and should not be circumvented. They encourage better code by forcing the author puts more thought into the dependencies between different parts. Allowing code to work in every way imaginable leads to spaghetti...

  • @Mark, Mike,

    Of course you can extract the algorithm into a method and assign the results to a readonly variable. But what if you have multiple readonly variables, and you add a constructor? I hope you remembered to assign all of the variables correctly. Or what if the initialization algorithm normally generates the values for all of the variables in a single pass? Now you have to run the algorithm multiple times instead of just once, or create a structure for the sole purpose of returning the results to the constructor so they can be assigned.

    The point is that there are numerous cases today where you have to sacrifice immutability or readability because of the restrictions of the language on constructors. I am NOT advocating allowing constructors to be called within a method body, even another constructor body, as I believe that would cause more problems than it would solve. However, I do think initialization methods such as I described earlier would be very useful. But I am not holding my breath.

  • David,

    I still disagree.  I'm not sure I understand your comment about remembering to assign all the variables correctly. If you are adding a new constructor...why not just call the existing constructor from yours?

    As for an initialization algorithm that generates values in a single pass: Either the algorithm should be split into independent methods that return individual values or, if the values and the algorithm are so intertwined as to preclude this, then I would make the argument the result should indeed be a struct as the values are obviously tightly related.

    I realize you are not advocating the current restriction be changed, but I don't see that anyone has made a compelling case that the existing restrictions are even an impediment...providing the design is carefully considered.

    Can I come up with a class that is difficult to initialize properly given the current language restrictions? Sure. Can I make a good case for designing such a class? Probably not.

  • A derived constructor calling the base constructor fails our code review.

    It's clever code but hard to debug when combined with similar techniques throughout a large system.  Such a system gets reputation of being difficult to maintain / debug.

Page 2 of 2 (28 items) 12