Downcasting a list of objects in C#

做~自己de王妃 提交于 2019-12-04 02:48:25

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:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!