IEnumerable<T> is a covariant interface. That means there's an implicit identity conversion from IEnumerable<T1> to IEnumerable<T2> so long as there is an implicit reference conversion or identity conversion from T1 to T2. See the documentation on conversions for more details on this terminology.
That's most often the case if T2 is a base class (direct or indirect) of T1, or is an interface that T1 implements. It's also the case if T1 and T2 are the same type, or if T2 is dynamic.
In your case, you're using an IList<Dog>, and IList<T> implements IEnumerable<T>. That means any IList<Dog> is also an IEnumerable<Dog>, so there's a implicit reference conversion to IEnumerable<Animal>.
A few important things to note:
Only interfaces and delegate types can be covariant or contravariant. For example, List<T> isn't covariant and couldn't be. So for example, you couldn't write:
// Invalid
List<Animal> animals = new List<Dog>();
Not all interfaces are covariant or convariant. For example, IList<T> isn't covariant, so this isn't valid either:
// Invalid
IList<Animal> animals = new List<Dog>();
Variance doesn't work with value types, so this isn't valid:
// Invalid
IEnumerable<object> objects = new List<int>();
Generic variance is only permitted where it's safe:
- Covariance relies on the type parameter only be present in an "out" position in any signature, i.e. values can come out of the implementation, but are never accepted.
- Contravariance relies on the type parameter only being present in an "input" position in any signature, i.e. values can be passed into the implementation, but are never returned
It gets a bit confusing when the signatures accept a parameter which is itself contravariant - even though a parameter is normally an "input" position, it's sort of reversed by contravariance. For example:
public interface Covariant<out T>
{
// This is valid, because T is in an output position here as
// Action<T> is contravariant in T
void Method(Action<T> input);
}
Definitely read the linked documentation for more information though!