thalm
thalm

Reputation: 2920

How to specialize an inherited List<SuperClass> to a List<SubClass> in C#?

This sounds like a common problem, so whats the best practice if you have a base class A with a public property of type List<SuperClass> and you inherit that list in class B but want to use a more specialized type parameter for the list:

class SuperClass 
{
    public bool Flag;
}

class SubClass : SuperClass 
{
    public int Number;
}

class A
{
    public List<SuperClass> Elements { get; }
}

class B : A
{
   public List<SubClass> Elements { get; set; }
}

So how can in overwrite the Elements list with a new list, but still make sure it will be accessed if another class only knows A objects?

class C
{
    List<A> AList;

    void FillList()
    {
        AList.Add(new B());
    }

    void DoSomething()
    {
        foreach (var a in AList)
            forach(var super in a.Elements)
                super.Flag = true;  
    }
}

Upvotes: 1

Views: 1259

Answers (2)

fliedonion
fliedonion

Reputation: 952

I gave it a try.

class Program {
    static void Main(string[] args) {

        var c = new C();
        c.FillList();
        c.DoSomething();


        Console.ReadKey();

    }
}


class C {
    List<A> AList;

    public void FillList() {
        AList = new List<A>();

        var a = new A();
        a.Elements = new List<SuperClass>() { new SubClass(), new SuperClass() };
        AList.Add(a);

        var b = new B();
        b.Elements = new List<SubClass>() { new SubClass(), new SubClass() };
        AList.Add(b);
    }

    public void DoSomething() {
        foreach (var a in AList)
            foreach (var super in a.Elements) { 
                super.Flag = true;
                Console.WriteLine(super.GetName());
            }
    }
}


class SuperClass {
    public bool Flag;
    public virtual string GetName() { return "super"; } 
}
class SubClass : SuperClass {
    public SubClass() { }
    public SubClass(SuperClass x) { }

    public int Number;
    public override string GetName() { return "sub"; } 
}

class A {

    public virtual IEnumerable<SuperClass> Elements {
        get{
            return elementList.AsEnumerable();
        }
        set {
            elementList = value.ToList();
        }
    }

    private List<SuperClass> elementList;
}

class B : A {

    public override IEnumerable<SuperClass> Elements {
        get {
            return elementList.AsEnumerable();
        }
        set {
            elementList = value.Aggregate(new List<SubClass>(), 
                                (acc, x) => {
                                    if (x is SubClass) 
                                        acc.Add((SubClass)x);
                                    else 
                                        acc.Add(new SubClass(x)); 
                                    return acc; });
        }
    }

    private List<SubClass> elementList;
}

I use IEnumerable. And convert IEnumerable<SuperClass> to List<SubClass> when setter property (of class B) was called.

Upvotes: 0

nvoigt
nvoigt

Reputation: 77294

You cannot. Because as it stands, if you insert a SuperClass through the A interface, B would fail, because SuperClass cannot be inserted into B's SubClass list.

The specific terms you should google are Covariance and Contravariance.

Your scenario can be solved if you restrict your classes to read access at least in the base class. Then you could have two read-only properties in your B class, one specialized, one not specialized but returning the specialized version.

Upvotes: 2

Related Questions