You are correct that this has to do with covariance; specifically it has to do with virtual method return type covariance, which is not a kind of covariance that the C# language supports.
UPDATE: This answer is over ten years old. C# may soon implement return type covariance. Please see https://github.com/dotnet/csharplang/issues/49 for details.
Note that even if it did, the system you describe is not type safe. Suppose we have:
interface IAnimal {}
interface IGiraffe : IAnimal {}
interface ITiger: IAnimal {}
class Tiger : ITiger {}
interface IHaveAnAnimal { IAnimal Animal { get; set; } }
class C : IHaveAnAnimal
{
public IGiraffe Animal { get; set; }
}
...
IHaveAnAnimal x = new C();
x.Animal = new Tiger(); // Uh oh. We just put a Tiger into a property of type IGiraffe.
Even if the covariance were legal at all, this kind of covariance would not be legal; you'd have to have no setter for the covariance to be legal.
Suppose then you did have no setter:
interface IAnimal {}
interface IGiraffe : IAnimal {}
interface ITiger: IAnimal {}
class Tiger : ITiger {}
interface IHaveAnAnimal { IAnimal Animal { get; } }
class C : IHaveAnAnimal
{
public IGiraffe Animal { get; }
}
Unfortunately this is still not legal. But you can do this:
class C : IHaveAnAnimal
{
IAnimal IHaveAnAnimal.Animal { get { return this.Animal; } }
public IGiraffe Animal { get; }
}
Now when C is used as a C, Animal returns a giraffe, and when used an an IHaveAnAnimal, it returns an IAnimal.