constructors are executed in the order from top to bottom I.E. base's first followed by derived one. This arangement is based on an important OOP assurance that an object (base here) must always be initialized before it can be used (here in derived class's constructor).
I'm wondering why field-initializers do not follow this principle in C#? Am I missing something here?
I've come across a usefulness of this principle with field-initializers as well. I have a base class with a property returning Identity object. Every derived class has its own repository field which I have been initializing using field-initializer (using default constructor). Recently I've decided that the repository class must also be provided with Identity object so I introduced an extra argument in the repository constructor. But I'm stuck to find out:
public class ForumController : AppControllerBase
{
ForumRepository repository = new ForumRepository(Identity);
// Above won't compile since Identity is in the base class.
// ... Action methods.
}
Now I'm left with only one option that is to plump my every controller with a default constructor for only to do the job of initializing of repository object with Identity.
In C#, a constructor runs the sequence:
Perform all field initializers Chain to base class constructor Execute user-supplied code for constructor
In vb.net, the sequence is:
Chain to base class constructor Perform all field initializers Execute user-supplied code for constructor
There's no Framework-based reason why C# does things in the order that it does, as evidenced by the fact that vb.net can and does do them in a different sequence. The design justification for the C# approach is that there should be no possibility of an object being exposed to the outside world before all field initializers (for derived- as well as base-class fields) have run; since a base-class constructor could expose an object to the outside world, enforcing that requirement means that field initializers must run before the base-class constructor.
Personally, I do not find that justification particularly convincing. There are many scenarios in which it will not be possible to set fields to useful values without having information which will not be available before the base constructor has run. Any code whose base constructor may expose partially-constructed instances needs to be prepared for that possibility. While there are times when it would be useful to specify that a field initializer should run "early", I think there are many more situations where it is helpful for them to be able to access the fledgeling object (in some measure because I believe that class fields whose values should be regarded as invariants during the lifetime of a class instance should when practical be set declaratively via initializers rather than imperatively within a constructor).
Incidentally, one feature I'd like to see in both vb.net and C# would be a means of declaring parameter-initialized fields and pseudo-fields. If a class has a parameter-initialized field of a certain name and type, every constructor for that class which does not chain to another of the same class must contain parameters with the appropriate names and types. The values of those fields would be set in the constructor before anything else is done, and would be accessible to other field initializers. Pseudo-fields would behave syntactically like fields, except that they would only be usable within field initializers, and would be implemented as local variables within constructors. Such a feature would make many types of structures more convenient. For example, if a type is supposed to hold one particular array instance throughout its lifetime, being able to say:
readonly param int Length; readonly ThingType[] myArray = new ThingType[Length];
would seem nicer than having to either construct the array in the class constructor (which couldn't happen until after the base constructor had run) or (for vb.net) having to pass the length to a base-class constructor which could then use it to set a field (which would then occupy space in the class even if its value--like Length
above--might be redundant).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With