Home > Software engineering >  How to increment List contents in derived classes?
How to increment List contents in derived classes?

Time:01-29

Given two properties in class A:

class A
{
    public virtual List<string> BaseStrings{get;}
    public virtual List<string> MyStrings{get;}
}

class B:A
{
    public override List<string> BaseStrings{//baseA.BaseStrings baseA.MyStrings}
    public override List<string> MyStrings{//?}
}

class C:B
{
    public override List<string> BaseStrings{//baseB.BaseStrings baseB.MyStrings}
    public override List<string> MyStrings{//?}
}

How do I craft the properties so that the BaseStrings accumulate in each derived class? I don't want to redeclare that structure in each derived class. I just want to ask for BaseStrings for the accumulated base strings.

EDIT: Based on some of the comments, the below classes achieve the goal. What pitfalls will I come across plastering new everywhere? It seems as if I cast D to any of it's previous forms like C, I will get exactly the set of strings I expect to see for a C. Yes!

I was hoping there was a way to write this and not be so redundant...

public abstract class A
{
    public virtual List<string> BaseStrings { get; } = new List<string>();
    public virtual List<string> MyStrings { get; } = new List<string>();
}

public class B : A
{
   public new List<string> BaseStrings => base.BaseStrings.Concat(base.MyStrings).ToList();

   public new List<string> MyStrings => new List<string>() { "dog", "cat" };
}

public class C : B
{
    public new List<string> BaseStrings => base.BaseStrings.Concat(base.MyStrings).ToList();

    public new List<string> MyStrings => new List<string>() { "Fox", "Hare" };
        
}

public class D : C
{
    public new List<string> BaseStrings => base.BaseStrings.Concat(base.MyStrings).ToList();

    public new List<string> MyStrings =>new List<string>{"Tortise","Fish"};
}

CodePudding user response:

[I] want each class to have a property that contains only it's strings, and a separate property that gives [me] it's strings plus it's parents' strings (recursively)

I don't think inheritance is the right way to do this - it sounds like you want encapsulation (a C has a B, which has a A). With inheritance, you have one instance that you can treat as either an A, a B, or a C. Different instances will not share the lists with each other.

That said, just for fun, you could do it by using new properties (not overridden) for each type:

class A
{
    public List<string> BaseStrings { 
        get {
            return this.MyStrings.ToList();
        }
    }
    public List<string> MyStrings{get; set;}
}

class B:A
{
    public new List<string> BaseStrings {
        get {
            return base.BaseStrings.Concat(this.MyStrings).ToList();
        }
    }
    public new List<string> MyStrings{get; set;}
}

class C:B
{
    public new List<string> BaseStrings { 
        get {
            return base.BaseStrings.Concat(this.MyStrings).ToList();
        }
    }
    public new List<string> MyStrings{get; set;}
}

Usage:


C c = new C();
B b = c;
A a = c;

c.MyStrings = new List<string> {"C"};
b.MyStrings = new List<string> {"B"};
a.MyStrings = new List<string> {"A"};

Console.WriteLine(string.Join(", ", a.MyStrings));
Console.WriteLine(string.Join(", ", a.BaseStrings));
Console.WriteLine(string.Join(", ", b.MyStrings));
Console.WriteLine(string.Join(", ", b.BaseStrings));
Console.WriteLine(string.Join(", ", c.MyStrings));
Console.WriteLine(string.Join(", ", c.BaseStrings));

Output:

A
A
B
A, B
C
A, B, C

CodePudding user response:

When aggregating values, don't use lists, as creating a new list for every sub class creates an extra useless iteration and allocation. Instead for purposes of aggregation, use IEnumerable<T>.

I think what you want is something like so:

class A
{
    public virtual IEnumerable<string> GetStrings() {
        yield return "Base String A";
        yield return "Base String B";
    }
}

class B:A
{
    public override IEnumerable<string> GetStrings() {
        foreach (string b in base.GetStrings())
            yield return b;
        yield return "My String C";
        yield return "My String D";
    }
}

class C:B
{
    public override IEnumerable<string> GetStrings() {
        foreach (string b in base.GetStrings())
            yield return b;
        yield return "My String E";
        yield return "My String F";
    }
}

If you really insist on using List properties, you can do that while still aggregating with IEnumerable, the below would work. Just remember that since you make MyStrings virtual, any implementation that overrides that now takes full ownership and potentially erases all previously held values in any sub classes. So C's overrid of MyStrings can completely wipe out all of B's held MyStrings values. this is why I would always prefer the first solution provided with IEnumerables all the way.

class A
{
    public virtual List<string> BaseStrings { get; }
    public virtual List<string> MyStrings { get; }
    
    public virtual IEnumerable<string> GetStrings() {
        foreach (string b in BaseStrings)
            yield return b;
        foreach (string m in MyStrings)
            yield return m;
    }
}

class B:A
{
    public override List<string> MyStrings {//?}
}

class C:B
{
    public override List<string> MyStrings {//?}
}

Now any time you want to grab all the strings, just use item.GetStrings()

  •  Tags:  
  • Related