开发者

Downcasting a list of objects in C#

How can I downcast a list of objects so that each of the objects in the list is downcast to an object of a derived class?

This is the scenario.

I have a base class with a List of base items, and two classes inheriting from it:

public class BaseClass
{
    public List<BaseItem> items;
    protected BaseClass()
    {
        // some code to build list of items
    }
}
public class DerivedClass : BaseClass
{
    public DerivedClass : base() {}
}
public class AnotherDerivedClass : BaseClass
{
    public AnotherDerivedClass : base() {}
}

public class BaseItem {}开发者_开发技巧
public class DerivedItem : BaseItem {}
public class AnotherDerivedItem : BaseItem {}

The idea is to not have to duplicate the code needed to build the list of items. The BaseItem has all the basic stuff I need, and I can always downcast BaseItem to one of the derived items.

The problem arises when I have a list of them. The List of BaseItem is declared in the BaseClass because all the derived classes have to have it. But when accessing it at runtime I can't seem to be able to downcast to the derived class.


Using LINQ:

    var baseList = new List<BaseClass>();
    var derivedList = baseList.Cast<DerivedClass>();

Note: Having to downcast usually is a 'smell' and indicates that the inheritance hierarchy is wrong, or wrongly implemented. The idea of having a base class is that you can treat all subclasses as superclass without having to downcast to individual subclass types.

Instead of Cast you might want to use OfType to 'fish out' certain derived classes from a collection of superclasses. But again, there should be no need to do that.

Ask yourself, why you need to have a subclass - maybe you need to move some functionality to base class?


I believe what you want to do is use generics:

public class BaseClass<T> where T: BaseItem, new()
{
    public List<T> items;
    protected BaseClass()
    {
        items = new List<T>();
        T item = new T();
        item.SomePropertyOfBaseItem=something;
        items.Add(item);
    }
}

public class DerivedClass: BaseClass<DerivedItem>

This will cause the items in DerivedClass to be List<DerivedItem>. The where enforces that only types that derive from BaseItem can be used.

edit: "downcasting", casting a type to a derived type, isn't really what you are trying to do here. Your intent is that the derived list objects use a specific derived item type by design, and presumably you want to store instantiated objects of the derived type in your derived list class.

So, this could work just fine without using generics: the List<BaseItem> is perfectly capable of storing any items that derive from BaseItem. However, you would have to reference these objects from the list using casting (as described in the other answers) in order to access the derived properties. But that is simply "casting" an object to it's true type. Generics gives you a way to provide strongly typed access to these objects directly.

Basically, storing an object in a container that is a superclass of the object doesn't change anything about the object - it only changes the way your code can refer to it, by making it appear to be the simpler type from which it derives.


public class Zoo
{
     public List<Animal> items;
     protected BaseClass()
     {         // some code to build list of items     }
 }

public class PettingZoo : Zoo
{
     public PettingZoo : base() {}
}

public class CatComplex : Zoo
{
     public CatComplex : base() {}
}

public class Animal {}
public class Sheep : Animal {}
public class Tiger : Animal {}

...

PettingZoo myZoo = new PettingZoo();
myZoo.items.Add(new Tiger());

PettingZoo is not interchangeable with Zoo, as a PettingZoo should restrict the types of Animals. As such, this design fails the Liskov substitution principle.


You can also use LINQ to iterate through the elements of the base class and downcast each element, like this:

var baseList = new List<BaseClass>();
var derivedList = new List<DerivedClass>();
baseList.ForEach(v => derivedList.Add((DerivedClass)v));

If you need to check the derived type of each element before casting:

var baseList = new List<BaseClass>();
var derivedList = new List<DerivedClass>();
baseList.OfType<DerivedClass>().ToList().ForEach(v => derivedList.Add(v));

For more information, take a look at the following articles:

  • DotNetPearls article on OfType
  • MSDN documentation on OfType
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜