Converting a List of Base type to a List of Inherited Type
Asked Answered
R

4

21

I would be certain that this question addresses something that would have been brought up in a previous question, but I was unable to find it.

There is a method in a C# class that takes as a parameter a generic List of a Base Class. I need to pass a list of an inherited class and do not know exactly how to do this. I am getting an error in my attempts. Below is sample code to illustrated this:

public class A
{
   public static void MethodC(List<A>)
   {
       // Do Something here with the list
    }
}
public Class B : A
{
   // B inherits from A, A is the Base Class   
}

// Code utilizing the above method 
List<B> listOfB = new List<B>();
A.MethodC( (List<A>) listOfB );  // Error: this does not work
A.MethodC( listOfB.ToList<typeof(A)>() ); // Error: this does not work
A.MethodC( listOfB.ConvertAll<A>(typeof(A)) ); // Error: this does not work
// how can I accomplish this?  It should be possible I would think

Note: Here is my final working Method as a reference. I got an even better solution to my problem, but technically it wasn't an answer to the question, since my question was phrased impropertly.

 public static DataTable 
    ObjectCollectionToDataTable<GLIST>
      (List<GLIST> ObjectCollection) where GLIST 
              : BaseBusinessObject
        {
            DataTable ret = null;

            if (ObjectCollection != null)
            {
                foreach ( var b in ObjectCollection)
                {

                    DataTable dt = b.ToDataTable();
                    if (ret == null)
                        ret = dt.Clone();
                    if (dt.Rows.Count > 0)
                        ret.Rows.Add(dt.Rows[0].ItemArray);
                }
            }

            return ret;
        }
Rosamariarosamond answered 26/3, 2009 at 15:36 Comment(2)
That's the same answer I gave you...Development
And it is technically the answer to what you originally asked, as well! :)Development
T
34

If you have linq available you can do

var ListOfA = ListOfB.Cast<A>().ToList();
Tannertannery answered 26/3, 2009 at 15:38 Comment(5)
I do not think I have linq, but it worked anyway. thank you very much for your quick response.Rosamariarosamond
Possible problem - does MethodC need to modify the list, so that those changes are visible outside of MethodC?Development
Glad it worked! The only reason I believe you have the LINQ extension methods available was the use of ToList in your example.Tannertannery
now, it doesn't need to modify the list, it foreachs through the collection and calls abstract methods on each one.Rosamariarosamond
You can skip the '.Cast<A>': var ListOfA = ListOfB.ToList<A>();Poinsettia
D
9

You cannot do that. To understand why it is not allowed, imagine what would happen if Add was called on a List<Derived> after it had been cast to a List<Base>.

Also, the answers implying that C# 4.0 will be different are wrong. List will never be modified to allow you to do this. Only IEnumerable will - because it does not allow items to be added to the collection.

Update: The reason it works in the solution you've gone for is because you're no longer passing the same list. You're creating a whole new list which is a copy of the original. This is why I asked about modifying the list; if MethodC makes changes to the number of items on the list, those changes would be made to a copy, not the original list.

I think the ideal solution for you is as follows:

public abstract class A
{
    public void MethodC<TItem>(List<TItem> list) where TItem : A
    {
        foreach (var item in list)
            item.CanBeCalled();
    }

    public abstract void CanBeCalled();
}

public class B : A
{
    public override void CanBeCalled()
    {
        Console.WriteLine("Calling into B");
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<B> listOfB = new List<B>();

        A a = new B();

        a.MethodC(listOfB);
    }
}

Notice how, with this solution, you can pass a List<B> directly to MethodC without needing to do that weird conversion on it first. So no unnecessary copying.

The reason this works is because we've told MethodC to accept a list of anything that is derived from A, instead of insisting that it must be a list of A.

Development answered 26/3, 2009 at 15:41 Comment(2)
I don't think that is true, it works fine. My method call works loops through the collection and acts on abstract (override) methods and properties. I used the code above in a previous response, and it works great. the project is .NET 2.0 with CSC.exe (C#) 3.0 as the compiler.Rosamariarosamond
I'm guessing you're having difficulty getting this to work? :)Development
V
4

You are addressing the lack of covariance in the current C# version. Here is one way of doing it:

listOfB.Cast<A>();
Vulcanology answered 26/3, 2009 at 15:39 Comment(2)
This only gives you an IEnumerable<A>, not List<A>.Desist
this works because ((List<A>)listOfB.Cast<A>()) gives me what I want when I explicitly cast it as a List.Rosamariarosamond
F
2

Here is an answer that will exclude any objects in your list of the wrong type. This is a much safer way in my opinion:

List<A> ListOfA  = ListOfB.OfType<A>().ToList();

The OfType method will exclude items of the wrong derived class where as the Cast will throw an error.

Fimbria answered 27/11, 2014 at 15:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.