views:

903

answers:

3

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;
        }
+7  A: 

If you have linq available you can do

var ListOfA = ListOfB.Cast<A>().ToList();
Quintin Robinson
I do not think I have linq, but it worked anyway. thank you very much for your quick response.
stephenbayer
Possible problem - does MethodC need to modify the list, so that those changes are visible outside of MethodC?
Daniel Earwicker
Glad it worked! The only reason I believe you have the LINQ extension methods available was the use of ToList in your example.
Quintin Robinson
now, it doesn't need to modify the list, it foreachs through the collection and calls abstract methods on each one.
stephenbayer
Then see my updated answer...
Daniel Earwicker
+3  A: 

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

listOfB.Cast<A>();
Andrew Hare
This only gives you an IEnumerable<A>, not List<A>.
tvanfosson
this works because ((List<A>)listOfB.Cast<A>()) gives me what I want when I explicitly cast it as a List.
stephenbayer
@tvanfosson - Nice catch!
Andrew Hare
+2  A: 

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.

Daniel Earwicker
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.
stephenbayer
See update above.
Daniel Earwicker
I'm guessing you're having difficulty getting this to work? :)
Daniel Earwicker