djdd87
djdd87

Reputation: 68466

Why can I use (almost) any type in a foreach over a list of unrelated interfaces?

With the first foreach in the below example, I'm sure it should throw a compilation error, but they build fine. Issues only occur at run time when it throws an invalid cast exception (as you'd expect):

public interface IState
{
    int Id { get; }
    string Description { get; }
}

public class Dog
{
    public string Name { get; set; }
    public string Breed { get; set; }
}

public class Cat
{
    public string Name { get; set; }
    public string Breed { get; set; }
}

public void Foo()
{
    IEnumerable<IState> states = new List<IState>();
    foreach (Dog dog in states) { } // Does not break in the compiler

    IEnumerable<Cat> cats = new List<Cat>();
    foreach (Dog dog in cats) { } // Obviously breaks in compiler
}

Why does this get passed the compiler? Dogis completely unrelated to IState, and if I tried to iterate through a list of Cat instead then the compiler will obviously catch it.

Upvotes: 3

Views: 70

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500385

The compiler is implicitly converting a cast from the element type to the type you've specified in the foreach statement.

So if you have an IEnumerable<Foo> and you use it as:

foreach (Bar bar in foos)

then that will insert a cast from Foo to Bar. This will compile if and only if that cast would normally be valid.

It isn't valid to cast from Cat to Dog because they're unrelated types. A Cat reference can never be valid as a Dog reference.

If is valid to cast from IState to Dog because it could be valid:

public class DogState : Dog, IState
{
    public int Id => 5;
    public string Description => "Dog state!";
}

IState state = new DogState();
Dog dog = (Dog) state; // Won't throw

In your collection example:

IEnumerable<IState> states = new List<IState> { new DogState() };
foreach (Dog dog in states)
{
    Console.WriteLine("I've got a dog state!");
}

Upvotes: 3

Related Questions