Reputation: 1271
It is known that in c# base class constructor is always called before inherited class constructor. I'm facing a problem because of that and I can't find how to get around it:
having a base class
public class BaseClass
{
public IValuesProvider ValuesProvider {get; protected set; }
public virtual IList<Value> Values
{
get {return ValuesProvider.GetAll(); }
}
public BaseClass(IValuesProvider valuesProvider)
{
ValuesProvider = valuesProvider;
Load();
}
public virtual void Load()
{
foreach (var value in Values)
{
// DO something with value
}
}
}
and an inherited class
public class InheritedClass : BaseClass
{
public int? Filter {get; protected set;}
public override IList<Value> Values
{
get {return base.Values.Where(v => v.Id > Filter); }
}
public InheritedClass (IValuesProvider valuesProvider, int filter)
: base (valuesProvider)
{
Filter = filter;
}
}
If I instanciate InheritedClass I will get a Null reference exception on Filter when trying to return Values. So I need to execute Load() when "Filter" is assigned and only once. I add to my constraints that InheritedClass can be inherited also like:
public class InheritedClass2 : InheritedClass
{
public int? Filter2 {get; protected set;}
public override IList<Value> Values
{
get {return base.Values.Where(v => v.Id > Filter2); }
}
public InheritedClass2 (IValuesProvider valuesProvider, int filter1, int filter 2)
: base (valuesProvider, filter1)
{
Filter2 = filter2;
}
}
How do I manage to call Load efficiently without writing dirty code ?
Upvotes: 3
Views: 1634
Reputation: 61
I had a similar problem and my solution was
public class BaseClass
{
private c_Text;
public List<int> c_Series;
public BaseClass(string _Text)
{
c_Text = _Text;
Init();
}
protected virtual void Init()
{
Initialize_Series();
}
protected virtual void Initialize_Series()
{
c_Series = Globals.Default_Series;
}
}
public class InheritedClass1 : BaseClass
{
protected c_Series1;
private Loaded = false;
public InheritedClass1(string _Text, List<int> _Input_Series1) : base(_Text)
{
c_Series1 = _Input_Series1;
Loaded = true;
Init();
}
protected override Init()
{
if(Loaded)
base.Init();
}
protected override void Initialize_Series()
{
c_Series = new List<int>(c_Series1);
}
}
public class InheritedClass2 : InheritedClass1
{
protected c_Series2;
private Loaded = false;
public InheritedClass1(string _Text, List<int> _Input_Series1, List<int> _Input_Series2) : base(_Text, _Input_Series1)
{
c_Series2 = _Input_Series2;
Loaded = true;
Init();
}
protected override Init()
{
if(Loaded)
base.Init();
}
protected override void Initialize_Series()
{
c_Series = new List<int>();
int l_Result = 0;
foreach(int l_List1_Int in c_Series1)
{
l_Result = l_List1_Int;
foreach(int l_List2_Int in c_Series2)
{
l_Result = l_Result + l_List2_Int;
c_Series.Add(l_Result);
}
}
}
}
This ensures that Init gets executed once after all constructors correctly initialized members even if you keep inheriting the classes. I dont know if you wouldconsider this solution "Dirty".
Upvotes: 0
Reputation: 146
You definitely have to move the load out of the constructor. The solution below would solve the problem, but would require you to keep a private list of values in your base class.
public class BaseClass
{
public IValuesProvider ValuesProvider { get; protected set; }
private bool isLoaded = false;
private List<Value> _values;
public virtual IList<Value> Values
{
get
{
if (!isLoaded)
{
_values = ValuesProvider.GetAll();
Load();
isLoaded = true;
}
return _values;
}
}
public BaseClass(IValuesProvider valuesProvider)
{
ValuesProvider = valuesProvider;
}
public virtual void Load()
{
foreach (var value in _values)
{
// DO something with value
}
}
}
I'm not sure why do you have Load as virtual. If you want to allow inheriting classes to specify that implementation, you can expose _values
through another protected property or method.
Upvotes: 1
Reputation: 921
In short, what you want to do is to have a constructor which set members variables and which do initialisation work too, and you want to ensure the order of this two treatments.
In these cases, it's common to use the Factory Pattern which help you separate this two kinds of treatments.
Read this article for exemple.
Good luck, may the sharp be with you.
Upvotes: 1
Reputation: 3101
I think lazy loading is the way to go here.
You can add a property
private bool loaded;
to your base class and add a method
protected void EnsureLoaded() {
if (!loaded) Load();
}
Load() should set loaded to true after loading the values in list which just holds all values, e.g. AllValues.
In your inherited class you do:
public override IList<Value> Values
{
get {
EnsureLoaded();
return base.AllValues.Where(v => v.Id > Filter);
}
}
Then whenever you want your values to be loaded call EnsureLoaded.
Upvotes: 0