2

Say I have the follow classes:

public class BaseClass {
    ...
}

public class ChildClass : BaseClass {
    ...
}

If I want to pass in single instance of ChildClass as a type BaseClass in a method call, I can do this just fine.

public void SomeCall(BaseClass baseClass)

....

ChildClass childClass = new ChildClass();
SomeCall(childClass);  <-- Works fine

However, if I want to pass in a List, I can't seem to cast it correctly.

public void SomeCall(List<BaseClass> baseClasses) 

....

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses);  <--  Gets compiler error

The error I'm getting is

Argument 1: cannot convert from 'System.Collections.Generic.List<ChildClass>'
  to 'System.Collections.Generic.List<BaseClass>'

Is there any way to cast the values IN the list to a type of the base class?

Right now, I'm thinking I just have to use a foreach and AutoMap each object in the list to a new list of BaseClass.

Scottie
  • 11,050
  • 19
  • 68
  • 109

4 Answers4

2

Your best bet is to use Generics here, something like:

public void SomeCall<T>(List<T> list) where T: BaseClass
{
   foreach(T item in list)
   {
       // Assumes BaseClass defines method doSomething
       item.doSomething(....);
   }
}

To call the method do

SomeCall<ChildClass>(listOfChildren);

Other alternatives:

You could just declare your list so that it is actually contined with a List<BaseClass>. e.g.,

// Assumes Children extends BaseClass
List<BaseClass> list = new List<Children>();

This may or may not be possible given your application, but it is easier if you've never used generics or can't use them (not sure if the syntax in the first part is valid below .NET 4.0.)

debracey
  • 6,517
  • 1
  • 30
  • 56
  • Generics are part of C# version (Visual Studio) it pays no weight to the version of .NET –  Sep 27 '13 at 00:27
  • Yeah, but I didn't know if the `where T: ...` bit was valid below 4.0, they added some new stuff co/contravariance in 4.0. – debracey Sep 27 '13 at 00:28
  • This one worked for me. I was able to do something like this: SomeCall(childClasses.ToList()); – Scottie Sep 27 '13 at 00:31
2

First, is there any reason that your method is public void SomeCall(List<BaseClass> baseClasses) instead of public void SomeCall(IEnumerable<BaseClass> baseClasses)? Unless you specifically need it as a List<BaseClass>, say to add things to it, you should pass it as an IEnumerable<BaseClass>, since this will give you the flexibility to do either:

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses.Cast<BaseClass>());

or:

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses.OfType<BaseClass>());

The Cast method will attempt to cast every element of the List into BaseClass, and will throw if any of the casts fail, while the OfType method will filter the Enumerable for instances that can be cast to BaseClass and only return those.

You can't cast the List like that because, as Eric Lippert answers in this answer, if SomeCall saw it as a List<BaseClass>, then it could add things to it that are not ChildClass. You could do:

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses.Cast<BaseClass>().ToList());

But that really isn't a good idea, since what you will be passing is actually a new List instance. The only reason that SomeCall's parameter would be typed as a List instead of IEnumerable is that it needed to modify the list, and if you pass it Cast<BaseClass>().ToList(), then the changes it makes will be lost immediately.

If you don't intend to make changes to the List, then it should be passed as an IEnumerable and cast dynamically using the above Linq methods. If you do intend to make changes, you should convert the list somewhere else and store it, so you have access to those changes.

Community
  • 1
  • 1
Mason
  • 703
  • 6
  • 20
0
public interface IBaseClass
{

}
public class BaseClass : IBaseClass
{

}

public class ChildClass : BaseClass, IBaseClass {

}

public void SomeCall(IBaseClass BassClass)
{
    BassClass.Dump();
}

public void SomeCall(List<IBaseClass> BassClasses)
{
    BassClasses.Dump();
}
void Main()
{
    List<IBaseClass> _ch = new List<IBaseClass>();
    _ch.Add(new ChildClass());
    _ch.Add(new ChildClass());
    _ch.Add(new ChildClass());

    SomeCall(_ch);
}

// Define other methods and classes here

Interfaces; This is a LinqPad example.

0

If your source list might contain different subtypes of the supertype the list contains, you can do something like this with Linq:

class SuperType
{
  ...        
}
class SubType : SuperType
{
  ... 
}

static void DoSomethingInteresting( List<SuperType list )
{
  List<SubType> desired = list.Select( x => x as SubType )
                              .Where( x => x != null )
                              .ToList()
                              ;
  ...
}
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135