I have a base class which implements property changed notifications. I've omitted the implementation technical details for clarity.
public class PersonDTO : INotifyPropertyChanged {
// in real these all have the backing field + equality check in setter + OnChanged call implementations
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
public PersonDTO() {
// initialize default values
// this invoke OnChanged so the object state can be maintained
Name = "New person";
Age = 30;
Gender = Gender.Female;
}
protected virtual void OnChanged(string propertyName) {
// raise PropertyChanged
}
}
I have another class which inherits from PersonDTO
and adds some properties.
public class PersonEditorModel : PersonDTO {
public BindingList<string> Titles { get; private set; }
private readonly IRepository _repository;
public PersonEditorModel(IRepository repository) {
_repository = repository;
}
protected override void OnChanged(string propertyname) {
if (propertyName == "Gender") {
// Here is a NullReferenceException
Titles.Clear();
if (Gender == Gender.Female) {
Titles.AddRange(new[] {"Ms", "Mrs"});
else
Titles.AddRange(new[] {"Mr", "Sir"});
}
// do some other things perhaps using the _repository (which would raise a NullReferenceException again)
}
}
The problem with this model is that in the base constructor, setting a property invokes the change notification, and the OnChanged
method in the descendant class executes when the descendant is not yet constructed (Titles
list is null).
Several ways I've been thinking of.
Use the backing fields in the base constructor. This would fix the exception, but the object state was not refreshed accordingly. I need a consistent state all the time, which means that the
Gender
and theTitles
should be in sync.Include a flag which means that the object is constructed, and check that flag in the
OnChanged
. This would be valid for simple cases like this, but what if I have a 3 level hierarchy. I would need to ensure that the flag is set when the bottom most constructor had finished running, which is not trivial.Use a factory method pattern in the base class where after construction I would call something like
StartChangeTracking()
. This is problematic because the descendant classes can have different constructor arguments, like in this case anIRepository
service. Also, the factory method pattern would make for example Json serialization/deserialization fairly harder (I mean for those classes without constructor arguments of course).