Simple simulation of animals eating other animals
every time I would need to add a new animal, I would not be able to do it just by extending the existing classes or by adding new ones: I would have to modify the existing classes.


The problem exists in both your approaches. It is because the eating preferences of individual species is mutualy dependent on all other existing animals. If you add new animal, you define what existing animals it likes to eat, but unless you modify existing animals, no existing animal will ever be interested in eating the new animal.

Generally relying on object being of a specific type is not polymorphism, it's a rape.

You need to abstract and centralize the eating preferences somehow.

public enum AnimalKind
{
  Lion,
  Hyena
}

public interface IDinner
{
  public AnimalKind Kind {get;}
  public Size Size {get;}
}

public interface IEater
{
  public bool Likes(IDinner dinner); 
}

public interface IAnimal : IEater, IDinner
{
  bool IsDead {get;}
  void Kill();
}

class Animal : IAnimal
{
  public AnimalKind Kind {get;}
  public Size Size {get;}
  public bool IsDead {get;}

  private IDictionary&lt;AnimalKind, Size&gt; Preferences {get;}

  public Animal(AnimalKind kind, Size size, IDictionary&lt;AnimalKind, Size&gt; preferences)
  {
    this.Kind = kind;
    this.Size = size;
    this.Preferences = preferences;
  }

  public bool Likes(IDinner dinner)
  {
     var maxSize = this.Preferences[dinner.Kind];
     return maxSize != null &amp;&amp; dinner.Size &lt;= maxSize;
  }

  public void Kill()
  {
    IsDead = true;
  }
}

public interface IAnimalFactory
{
  public IAnimal CreateAnimal(AnimalKind kind, Size size);
}

public class AnimalFactory : IAnimalFactory
{
  // centralized place for all animals and their eating preferences
}

public static class AnimalPark
{
    public static void Process(IList&lt;IAnimal&gt; animals)
    {
        int length = animals.Count;
        for(int i=1; i&lt;length; i++)
        {
            var eater = animals[i - 1];
            var dinner = animals[i];
            if (!dinner.IsDead &amp;&amp; !eater.IsDead &amp;&amp; eater.Likes(dinner)) {
                dinner.Kill();
            }
        }
    }
}


Adding new animal now means just extending AnimalKind enum and modifying AnimalFactory class to be aware of the new kind.

Although you might object that having the enum is also not nice. And I kinda agree, but I didn't want to invent an abstraction of the dinner description that was not provided by you. Maybe dinner description could instead be something like a description of how big teeth and paws the dinner has, how aggresive and vicious they look, if they have colors that imply being venomous, etc... If you impleent something like this and get rid of the AnimalKind enum, you will need to change the factory to contain a method per existing animal, but I would not consider it a problem, it may actually be so even with the enum existing...

Also notice I have moved the actual killing of the animal to the AnimalPark class, because that would be the same for all animals. An animal is always killed when eaten and stays alive when not eaten, right?