Identifying derived types from a list of base class objects
Asked Answered
R

5

5

This may seem kind of "homework-ish" and/or trivial, but it is for a real business purpose; it's just the easiest way I could think of to explain what I'm conceptually trying to do.

Suppose I have an Animal class, and some other classes (Bird, Cat, Kangaroo). Each of these inherits from Animal.

Animal might look like this:

public class Animal
{
    public Animal()
    {

    }

    public string Name { get; set; }
    public string AnimalType { get; set; }
    public List<Animal> Friends { get; set; }
}

Kangaroo might look like this:

public class Kangaroo : Animal
{
    public Kangaroo()
    {

    }

    public int TailLengthInches { get; set; }
}

Suppose the kangaroo has two friends, a bird and a cat. How could I add those to the list of "Animal" friends (as type Animal), but preserve the ability to access the properties of the derived classes? (as in, I still need to be able to work with properties that are specific to the type of animal, like FeatherColor for the bird, for example, even though they will be considered just "animals".

The reason I am trying to do this is that when I later get the list of the animal's "friends", it is vital that I know what type of animal the friend is. Depending on the animal type (whether it is a bird, cat, etc) I want to do something different (show a different template in on an ASP.NET page, actually, but I don't need help with that specifically).

I guess it boils down to this...if I have a list of Animal, how do I know what the derived type was for each of the objects in the list, so I can cast them back to their derived type or do something else that allows me to get to the specific, different properties that are on each derived type?

Thanks!

Rearward answered 15/1, 2010 at 21:6 Comment(1)
Tip: to get code to display correctly, use the 101010 button in the editor (or indent by four spaces).Democracy
H
8

You can use LINQ to filter on the type you're looking for

animals.Friends = new List<Animal> { new Kangaroo(), new Bird() };
foreach ( var kangaroo in animals.Friends.OfType<Kangaroo>()) {
  kangaroo.Hop();
}
Harl answered 15/1, 2010 at 21:17 Comment(1)
This is a really elegant way of handling it. Thanks!Rearward
D
2

Try casting it:

var friendBird = friend as Bird;
if (friendBird != null)
{
    // It's a bird, so do something with it
)
Discriminating answered 15/1, 2010 at 21:9 Comment(2)
+1 for using the as keyword to avoid the double casting involved with the is keyword and then casting the object.Germiston
Since C# 7.0 using is is better now :)Amersham
D
2

You can use typeof to get the actual type, or use the is or as operators to test and optionally cast:

foreach (Animal a in Friends)
{
  Kangaroo k = a as Kangaroo;
  if (a != null)
  {
    // it's a kangaroo
    k.JumpAround();
  }
  Ostrich o = a as Ostrich;
  if (o != null)
  {
    o.StickHeadInSand();
  }
}

(I haven't used else clauses here, which could cause errors if you're testing against types which might be in a hierarchy e.g. if you had both Kangaroo and Marsupial clauses, you probably would NOT want both to be executed!)

You can also do this with the is operator, which looks nicer, but will make FxCop cry:

foreach (Animal a in Friends)
{
  if (a is Kangaroo)
  {
    ((Kangaroo)a).JumpAround();
  }
}

In general, by the way, you probably want to look for an opportunity to put a virtual method on Animal and implement that method polymorphically, rather than having monster if-statements which result in brittle coupling to a specific set of derived classes.

Democracy answered 15/1, 2010 at 21:11 Comment(0)
B
0

Use the 'is' keyword.

if (animal is Bird) ...

Boxcar answered 15/1, 2010 at 21:12 Comment(0)
S
0
animals.Friends = new List<Animal> { new Kangaroo(), new Bird() };
foreach (Animal animal in animals.Friends)
{
if (animal is Kangaroo)
Console.WriteLine("Kangaroo");
else if (animal is Bird)
Console.WriteLine("Bird");
}
Sussman answered 15/1, 2010 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.