You've shown us a limited part of the Animal class.
IF this is the only impact your animal type has, i.e. telling you which animal it is, it is better to favor the enum here as the derived classes aren't really being using to their full potential.
However, this is likely not the case. There might be some more animal logic. For example:
public class Animal
{
public enum SpecificAnimal { Cat, Dog, Bird }
public SpecificAnimal AnimalType { get; set; }
public string SaySomething()
{
switch(this.AnimalType)
{
case SpecificAnimal.Dog:
return "Woof";
case SpecificAnimal.Cat:
return "Meow";
case SpecificAnimal.Bird:
return "Tweet";
default:
return "...";
}
}
}
Here, it would be better to use inheritance, because each animal type has custom logic that is specific to its type.
public class Animal
{
public virtual void SaySomething()
{
return "...";
}
}
public class Dog : Animal
{
public override void SaySomething()
{
return "Woof";
}
}
public class Cat : Animal
{
public override void SaySomething()
{
return "Meow";
}
}
public class Bird: Animal
{
public override void SaySomething()
{
return "Tweet";
}
}
It may seem overkill for the example I used here, but in the real world your custom logic is going to be significantly more impactful than just returning a hardcoded string.
You don't want to have to stack all dog/cat/bird/... logic into a single class, because it gets way too cumbersome to juggle them all and not get distracted by them at the same time.