Why are field initializers of a derived class are executed before base classes's initializers
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 For开发者_运维知识库umController : 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).
Field initialisers are executed in the constructor, and as the constructor in the base class is called first, all field initialisers are also executed before the derived constructor executes.
Example:
public class Base {
// field initialiser:
private string _firstName = "Arthur";
public string FirstName { get { return _firstName;}}
public string LastName { get; private set; }
// initialiser in base constructor:
public Base() {
LastName = "Dent";
}
}
public class Derived : Base {
public string FirstNameCopy { get; private set; }
public string LastNameCopy { get; private set; }
public Derived() {
// get values from base class:
FirstNameCopy = FirstName;
LastNameCopy = LastName;
}
}
Test:
Derived x = new Derived();
Console.WriteLine(x.FirstNameCopy);
Console.WriteLine(x.LastNameCopy);
Output:
Arthur
Dent
Field Initializers are not meant to be a replacement for constructors.
As per MSDN documentation all field initializers are executed before constructors. However a restriction on the Field Initializer is that they cannot refer to other instance fields. ( http://msdn.microsoft.com/en-us/library/ms173118(v=vs.80).aspx)
The restriction is due to the fact that there is no way to identify the right order and the dependencies to execute the Field Initializers at a compiler level.
You would have to write a default constructor to achieve what you want. You could possibly try with a static field, however; it is a bad practice and might not even suit your design.
Edit for clarifying question asked in comment:
As far as a derived class is concerned, the base class fields look the same whether they are initialized through the initializer or the constructor. This information cannot be supplied to the child classes as this would mean exposing the implementation of the base class (which is strictly private to the base class).
This implies that the derived class Initializer has no way to figure out if all the base class fields have been initialized before accessing them. Hence the behavior is not allowed.
精彩评论