0

I have a class A from which B and C inherit.
I have two lists: listB and listC, of the respective types.

I want to make a method that returns the two lists inside an array, like so:

public override List<A>[] GetAllItems()
{
    return new List<A>[2]
    {
        listB,
        listC
    };
}

However, when I try this approach, I get the following error, because I try to convert the inherited types incorrectly.

Cannot implicitly convert type 'System.Collections.Generic.List<Lae.B>' to 'System.Collections.Generic.List<Lae.A>' [Assembly-CSharp]csharp(CS0029)

Is there some way to create this array without converting the elements?


Note: I am not using a struct or class, because I want the array to be of various sizes based on logic above.

7
  • I would suggest to use interfaces for that is the list objects are somewhat similar like id's,... if not you can use wrapper classes for the child lists Commented Apr 19, 2022 at 11:07
  • 2
    You can listB.Cast<A>().ToList(), unless that is what you mean by "converting the elements" - I don't class a cast as a conversion in this sense because your Bs in your listB remain Bs (look like A but can be cast back to B) rather than becoming As (A cannot be cast to B) but your opinion may differ Commented Apr 19, 2022 at 11:07
  • 2
    Perhaps keeping them separated would more easily achieve goals at the caller end; return a tuple of (List<B>,List<C>) instead.. Commented Apr 19, 2022 at 11:12
  • Could GettAllItems return a IReadOnlyList<A>[]? Or does each list need to remain mutable when accessed via the array? Commented Apr 19, 2022 at 11:17
  • 1
    @Lae won't I lose information and functionality? - no. object o = new StringBuilder(); (o as StringBuilder).Append("Hello world"); - it is still a StringBuilder inside o, it still has its text "Hello world", and it can be treated as a stringbuilder at any time by casting o to a StringBuilder. It is of course, a massive pain in the ass to cast all the time and makes for ugly code; you'd probably want to cast back.. But if you were going to do that you might as well just use the tuple I suggested and skip that "cast it all as A just to get it out of the method then uncast as B/C again" Commented Apr 19, 2022 at 13:45

2 Answers 2

1
public List<A>[] GetAllItems()
{
    var result = new List<A>[2] {
        listB.Cast<A>().ToList(),
        listC.Cast<A>().ToList(),
    };
    
    return result;
}

If you need to return array of Lists - easiest way is to use Cast linq extension method.

In reference to the comments you have to remember that if you modify listB or listC, the change won't be reflected in the casted collections.

Anyway, if you need to have an access to the original listB / listC collections references, you can use IEnumerable instead of List in order to not be forced to "materialize" the results. Example:

public IEnumerable<A>[] GetAllItems()
{
    return new IEnumerable<A>[] {
        listB,
        listC,
    };
}

Now when you access eg. allItems[0] it will reference to the original listB collection.

Sign up to request clarification or add additional context in comments.

5 Comments

Well, you don't have to - there are other ways to skin that cat
@CaiusJard Since I am casting from B back to A, won't I lose information and functionality?
@CaiusJard of course i agree with you. Anyway, i think this is the way that is "closest" to the code from the question.
Also not that this creates 2 new lists, and updating the original listB / listC will have no impact on the object returned from GetAllItems.
Regarding casting, I think this answer explains it pretty well.
1

You can't do that because instance of B is an also A but instance of List of B is not also List of A.

You should box the types in the collection, you can use Cast function of in the Linq namespace.

using System.Linq;

 List<A>[] GetAllItems()
   {
    var result = new List<A>[2] {
        listB.Cast<A>().ToList(),
        listC.Cast<A>().ToList(),
    };
    return result;
   }

Or you can do that manualy.

List<A>[] GetAllItems()
{
    var boxedListB = new List<A>();
    var boxedListC = new List<A>();
    foreach (var item in listB)
    {
        boxedListB.Add(item);
    }
    foreach (var item in listC)
    {
        boxedListC.Add(item);
    }
    var result = new List<A>[2] {
         boxedListB,
         boxedListC
     };

    return result;
}

Or you can use Select function in System.Linq namespace.

List<A>[] GetAllItems()
{

    var result = new List<A>[2] {
         listB.Select(x=> x as A).ToList(),
         listC.Select(x=>x as A).ToList()
     };

    return result;
}

You can check this document for more information about boxing/unboxing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.