C# - 用于保存从同一基类继承的多个对象类型的列表

时间:2014-01-31 18:15:03

标签: c# list oop

在下面的例子中,我希望能够将StandardCar或RaceCar的实例放入CarCollection列表,然后在循环通过CarCollection列表时将它们转换为正确的数据类型。

我理解这在C#中是不可能的。但是,我无法找到一种方法,并不涉及一个在一个类中具有所有属性的类。由于NumberOfRaces仅适用于RaceCars,这样看起来很麻烦吗?

当我循环收集时,我会知道我是否正在使用标准汽车或赛车。但是如果我将StandardCar放入列表中,那么目前使用以下内容,然后循环遍历该列表并将其转换为StandardCar,我将获得强制转换异常。

请注意我的实际最终课程比下面的课程要复杂得多。

class Car
{
    public string Name {get; set;}
}

class RaceCar : Car
{
    public int NumberOfRaces {get; set;}
}

class StandardCar: Car
{
    public bool ChildrenInCar {get; set;}
}    

class CarCollection
{
   List<Car> Collection {get; set;}
}

感谢您的帮助。

6 个答案:

答案 0 :(得分:3)

完全有可能,您可以像往常一样添加到列表中,但要退回将需要测试返回的汽车类型:

// Create the cars
RaceCar rcar = new RaceCar();
StandardCar scar = new StandardCar();

// Add to the collection
CarCollection col = new CarCollection();
col.Add(rcar);
col.Add(scar);

// Iterate
foreach(Car car in col.Collection)
{
    if(car is RaceCar)
    {
        RaceCar racecar = (RaceCar)car;
    }
    else if(car is StandardCar)
    {
        StandardCar standCar = (StandardCar)car;
    }

    // ... etc
}

你也可以这样做:

car as RaceCar

并确保返回的值不为null。

注意:代码未经过测试,但应该可以使用。

答案 1 :(得分:2)

您可以检测演员是否有效:

List<Car> Collection;

..

foreach (Car car in Collection)
{
    RaceCar raceCar = car as RaceCar;
    if (raceCar != null)
    {
       raceCar.NumberOfRaces++;
    }
}

真的,这很糟糕。如果可能的话,最好不要“关心”具体派生类型是什么。例如:

foreach (Car car in Collection)
{
    car.Update();
}

然后RaceCar会有自己的Update()实现,可以使用NumberOfRaces。此外,您可能不需要NumberOfRaces属性来设置setter,也许您甚至不需要公开。

答案 2 :(得分:1)

是的,您可以将StandardCar添加到List<Car>集合中。在迭代集合时,您不能做的是轻松访问派生类特定属性。

var collection = new CarCollection();
collection.Collection= new List<Car>();

collection.Collection.Add(new RaceCar());
collection.Collection.Add(new StandardCar());

foreach (var car in collection.Collection)
    Console.WriteLine(car.Name);

工作得很好。你不能做的是:

foreach (var car in collection.Collection)
    Console.WriteLine(car.ChildrenInCar);

因为ChildrenInCar仅存在于StandardCar

答案 3 :(得分:0)

处理这个问题有不同的可能性。只要有可能,你应该尽量避免铸造。例如,如果要以不同方式显示不同的汽车,请覆盖ToString方法。您还可以添加以不同方式响应不同情况的方法。在基类中将这些方法声明为抽象(并且将基类delcare作为抽象)。

abstract class Car
{
    public string Name { get; set; }

    public abstract void RespondToCollision();
    public abstract void RespondToLoopPerformed();
}

class RaceCar : Car
{
    public int NumberOfRaces {get; set;}

    public override void RespondToCollision()
    {
        // Do nothing
    }

    public override void RespondToLoopPerformed()
    {
        NumberOfRaces++;
    }

    public override string ToString()
    {
        return String.Format("Name = {0}, # of races = {1}", Name, NumberOfRaces);
    }
}

class StandardCar: Car
{
    public bool ChildrenInCar {get; set;}

    public override void RespondToCollision()
    {
        if (ChildrenInCar > 0) {
            ChildrenInCar--;
        }
    }

    public override void RespondToLoopPerformed()
    {
        // Do nothing
    }

    public override string ToString()
    {
        return String.Format("Name = {0}, children in car = {1}", 
                             Name, ChildrenInCar);
    }
}    

另一种可能性是测试实际类型:

if (car is RaceCar) {
    ((RaceCar)car).NumberOfRaces++;
} else if (car is StandardCar) {
    ((StandardCar)car).ChildrenInCar = 0;
}

另一种可能性是过滤汽车类型并单独处理:

var raceCars = cars.OfType<RaceCar>();
foreach (raceCar in raceCars) {
   raceCar.NumberOfRaces++;
}

答案 4 :(得分:0)

另一种设计是使用接口为各种汽车对象指定不同的逻辑边界。

interface ICar ...
interface IRacingCar : ICar
interface IFlyingCar : ICar
interface IFamilyCar : ICar

然后在你的循环中,你可以根据他们的逻辑能力操作各种汽车。

void ProcessCars()
{
    foreach( var car in cars )
    {
         if(car is IRacingCar) ...
         if(car is IFlyingCar) ...
         if(car is IFamilyCar) ...
    }
}    

这种方法的优点是你可以拥有更多类型的类,而不必为每个类都有一个if语句。此外,您的if语句一次只需处理一个问题。

class RaceCar: IRacingCar ...
class FlyingRaceCar: IRaceCar, IFlyingCar ...
class FlyingFamilyCar: IFlyingCar, IFamilyCar ...

循环并不关心单个汽车可以实现多个接口。它只需要同时处理一个问题。

例如,任何IFlyingCar都可以处理重力和空中交通管制,无论是否有儿童乘客或已经完成了多少次比赛。

这是接口的优点。它们允许您的界面使用者仅关注对象的质量。

答案 5 :(得分:-2)

创建一个界面:

public interface ICar{
//Place all common property / method definitions here
}

让Car实现该界面:

public class Car : ICar
{
//implementation
}

然后改变你的CarCollection:

public class CarCollection
{
List<ICar> Collection{get; set;}
}

现在,您应该能够将具有Car基础的任何内容添加到该列表中。枚举您的集合时,所有ICAR都将具有您可以访问的界面中定义的属性/方法,而无需进行强制转换和类型检查。