Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

List<Parent> holding Child element without losing properties C#

I have these classes

class Start
{
    public List<Base> list { get; set; }
    public Start() 
    {
        list = new List<Base>();
    }
}

public abstract class Base
{
    public int a { get; set; }
}

class B : Base
{
    public int b;
    public B(int a, int b) { this.a = a; this.b = b; }
}

class C : Base
{
    public int c;
    public C(int a, int c) { this.a = a; this.c = c; }
}

I want list property of class Start to hold instances of class B or instances of class C (not both together, but it may hold the same type of any of B or C)
If possible, I don’t want to use Generics
In C#, This is possible:

List<Object> lst = new List<Object>();
lst.Add(1);
list.Add("Text");
Console.WriteLine("{0} {1}", lst[0], lst[1]);

I don’t understand why I can’t make a similar behavior here:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Start s = new Start();
B b = new B(1, 2);
s.list.Add(b);
Console.WriteLine(s.list[0].a); //works
Console.WriteLine(s.list[0].b); //doesn't work

>Solution :

The difference between the two snippets is that in the first one you are not accessing any type-specific information (fields/properties/methods), i.e. something like the following will not compile too:

List<Object> lst = new List<Object>();
lst.Add(1);
list.Add("Text");
// will not compile despite string having Length property:
Console.WriteLine("{0} {1}", lst[0], lst[1].Length); 

a is common property declared in Base class, so it is available for every child of Base, if you want to access child specific properties you need to type test/cast :

Start s = new Start();
B b = new B(1, 2);
s.list.Add(b);
Console.WriteLine(s.list[0].a); //works
if(s.list[0] is B b)
{
    Console.WriteLine(b.b);
}

or make Start generic:

class Start<T> where T: Base
{
    public List<T> list { get; set; }
    public Start() 
    {
        list = new List<T>();
    }
}

var s = new Start<B>();
s.list.Add(new B(1, 2));
Console.WriteLine(s.list[0].b);

P.S.

Note that overriding ToString in Base, B and A will make Console.WriteLine("{0}", s.list[0]); "work":

class B : Base
{
    // ...
    public override string ToString() => return $"B(A: {a} B: {b})";
}

class C : Base
{
    // ...
    public override string ToString() => return $"C(A: {a} B: {c})";
}

Start s = new Start();
B b = new B(1, 2);
s.list.Add(b);
s.list.Add(new C(4, 2));
Console.WriteLine("{0} {1}", s.list[0], s.list[1]); // prints "B(A: 1 B: 2) C(A: 4 B: 2)"

So possibly you can introduce some method in Base which will allow you to use List<Base> (hard to tell without knowing actual use case).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading