I have a class A
from which B
inherits.
A
has a property called Data
which is a class that contains some data. There is also BData
that again inherits from data
.
In other words:
class A
{
public Data data;
}
class B : A
{
public new BData data;
}
class Data
{
public string id = "something";
}
class BData : Data
{
public new string id = "something else";
}
Now, I use a method to read the data from any class and do some logic based on the data.
public bool ReadData<T>(T element) where T : A
{
Console.WriteLine($"Element: {element}");
Console.WriteLine($"Element ID: {element.data.id}");
//do some logic
}
I make a test element for A
and B
and I pass it to the method like so:
A a = new A();
a.data = new Data();
B b = new B();
b.data = new BData();
ReadData<A>(A);
ReadData<B>(b);
The output I get is this:
Element: A
Element ID: something
Element: B
Element ID:
I can not understand why when I pass the second object, it has a null property. I suspect it might have something to do with the different types (need casting from B
to A
? is it because I'm hiding Data
with BData
?) but I can not figure it out. Am I missing something?
What causes this to happen and how can I fix it?
CodePudding user response:
This
class B : A
{
public new BData data;
}
doesn't make the old data
field in
class A
{
public Data data;
}
class disappear. It doesn't replace it, it simply hides it. Then
public bool ReadData<T>(T element) where T : A
treats every object as A
. In particular when you initialize B
then its public Data data
field (of the base A
class) is not initialized and hence stays null. Then even though you pass B
, it actually tries to access its data
field of A
class.
The behaviour is easier to understand if you change the B
to
class B : A
{
public BData data2;
}
The moral of this story is: new
keyword is dangerous, avoid it.
Here's how you can achieve what you want with properties instead of fields. Something like this should work:
class A
{
public Data data { get; set; }
}
class B : A
{
public new BData data {
get {
return (BData)base.data;
}
set {
base.data = value;
}
}
}
But I encourage you to use a common interface for both B
and A
instead of such tricks.