如何在List<T>中获取特定的派生对象?

7

假设我有一个通用的Fruit列表(List<Fruit> fruits = new List<Fruit>())。然后我添加了一些对象(都派生自Fruit)- BananaAppleOrange,但这些派生对象具有不同的属性(例如Banana.IsYellow)。

List<Fruit> fruits = new List<Fruit>();
Banana banana1 = new Banana();
Banana banana2 = new Banana();
Apple apple1 = new Apple();
Orange orange2 = new Orange();

fruits.Add(banana1);
fruits.Add(banana2);
fruits.Add(apple1);
fruits.Add(orange1);

然后我可以这样做:
foreach(Banana banana in fruits)
    Console.Write(banana.IsYellow);

但是在执行时,这是无效的,因为苹果和橙子对象上没有 IsYellow 属性。

我如何从 List<Fruit> 中仅获取香蕉、苹果、橙子等水果?


有人知道如何在C++中实现吗?我也陷入了这个困境,但我正在用C++编写我的代码! - Joy
5个回答

30
foreach(Banana b in fruits.OfType<Banana>())

哇,我不知道那个。很好! - Aistina

3
你可以直接进行以下操作:

foreach(Fruit fruit in fruits)
{
   Banana b = fruit as Banana;
   if(b != null)
   {
      Console.Write(b.IsYellow);
   }
}

3
步骤1: 首先,您应该从水果列表中创建子列表。使用泛型的FindAll()Predicate函数来创建子列表。
步骤2: 接下来,在仅包含“香蕉”的子集中进行迭代。
以下是代码:
步骤1:
List<Fruit> fruits = new List<Fruit>();
Banana banana1 = new Banana();
Banana banana2 = new Banana();
Apple apple1 = new Apple();
Orange orange1 = new Orange();

fruits.Add(banana1);
fruits.Add(banana2);
fruits.Add(apple1);
fruits.Add(orange1);

//Extract Banana from fruit list
List<Fruit> bananaComb = fruits.FindAll(IsBanana);

//Now iterate without worring about which fruit it is
foreach (Fruit fruit in bananaComb)
{
    Console.WriteLine(((Banana)fruit).IsYellow);
}

步骤2:接下来是谓词函数
//A Predicate function to determine whether its a Banana
static protected bool IsBanana(Fruit aFruit)
{
    return aFruit.GetType().Name == "Banana" ? true : false;
}

是的,那样做可以行得通,但是我正在使用这个作为根UnitOfWork/Repository上下文,因此“水果列表”将包含许多对象,在该上下文中复制它并不是一个好主意。 - user121292
1
不用担心。子列表并不是真正的副本,而只是保存指向主列表上“香蕉”元素的指针。我已经检查过我的示例代码。要了解更多关于List<t>.FindAll()的信息,请参考 http://msdn.microsoft.com/en-us/library/fh1w7y8z.aspx - Kaz

1

个人而言,我觉得这种方法更易读:

foreach(Fruit fruit in fruits)
{
   if (fruit is Banana)
   {
      Banana b = fruit as Banana;
      Console.Write(b.IsYellow);
   }
   else if (fruit is Apple)
   {
      // ...
   }
}

然而,您将会受到一定的性能损失,并且代码分析警告会告诉您不要进行不必要的强制转换,因为您实际上是在做两次。 - Ian
1
啊,循环的if-then-else反模式[紧密相关于循环的case语句]。我的眼睛都要流血了。不是因为你这里的例子,而是这些东西会演变成难以维护的恶性肿瘤。 - Dan Blair
以上评论很有道理,但是是否有更好的方法可以避免在Ian的示例中进行空值检查。这就是让我眼花缭乱的部分。 - NascarEd
我们正在谈论水果,IsYellow是对象状态的派生属性(颜色)。如果它们都有颜色(它们可能应该有),则检查它。通常情况下,这种模式被用来取代继承,虽然并非所有情况都错,但大部分时间(大多数?)似乎是如此。请参见Fowler的《重构》。 - Dan Blair

0

添加另一种语法,不过.OfType<Banana>() 可能是最好的选择。

foreach (Banana b in fruits.Where(x => x is Banana))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接