Downcasting a list of objects in C#

ぃ、小莉子 提交于 2019-12-21 09:59:21

问题


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.


回答1:


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?




回答2:


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.




回答3:


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.




回答4:


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


来源:https://stackoverflow.com/questions/5234111/downcasting-a-list-of-objects-in-c-sharp

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