Home > Mobile >  Why does the property become null when passed as a parameter?
Why does the property become null when passed as a parameter?

Time:04-20

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.

  • Related