views:

285

answers:

5

Let's say I have a generic list of Fruit (List<Fruit> fruits = new List<Fruit>()). I then add a couple of objects (all derived from Fruit) - Banana, Apple, Orange but with different properties on the derived objects (such as 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);

Then I can do this:

foreach(Banana banana in fruits)
    Console.Write(banana.IsYellow);

But at execution time of course this is not valid because there is no IsYellow-property on the Apple and Orange objects.

How do I get only the bananas, apples, oranges etc from the List<Fruit>?

+30  A: 
foreach(Banana b in fruits.OfType<Banana>())
Mehrdad Afshari
Wow, I didn't know that one. Nice!
Aistina
+2  A: 

You could just do

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

Personally, I find this method more readable:

foreach(Fruit fruit in fruits)
{
   if (fruit is Banana)
   {
      Banana b = fruit as Banana;
      Console.Write(b.IsYellow);
   }
   else if (fruit is Apple)
   {
      // ...
   }
}
NascarEd
You will however receive a slight performance hit and a code analysis warning telling you not to cast unneccessairly because you are essentially doing it twice.
Ian
Ahh, the looping if then else anti pattern [close cousin to the looping case statement]. My eyes they bleed. Not because of your example here, but these things grow into un maintainable cancers.
Dan Blair
The above comments make sense, but is there something better that avoids the null check in Ian's example. That's the part that makes MY eyes bleed.
NascarEd
We are talking about fruit, and IsYellow is a derived property of an object state (Color or Colour). If they all have a Color (which they probably should) then check it. Generally this pattern is being used instead of using inheritance, not all cases of this are wrong but a substantial amount of the time (most?) it sure seems to be. SEE Refactoring by Fowler.
Dan Blair
+2  A: 

Step 1: First you should make sub-list from Fruit list. To make the sub-list use Generic's FindAll() and Predicate function.

Step 2: Later, in the sub-set you can iterate, which contains only 'Banana'

Here is the code

Step1:

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);
}

Step 2: Here comes predicate function

//A Predicate function to determine whether its a Banana
static protected bool IsBanana(Fruit aFruit)
{
    return aFruit.GetType().Name == "Banana" ? true : false;
}
Kaz
Yeah, that would work, however I'm using this is a root UnitOfWork/Repository context so the "list of fruit" is going to contain a lot of objects, making a copy of that isn't a good idea in that context.
antirysm
Don't worry. The sub-list is not really a copy, instead, it just hold the pointers that point to 'Banana' on main list.I already check thru my sample codeTo know more about List<t>.FindAll() referhttp://msdn.microsoft.com/en-us/library/fh1w7y8z.aspx
Kaz
+1  A: 

adding another syntax, though .OfType< Banana >() is probably the best.

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