Почему я не могу передать List <List <Foo>> в IEnumerable <IEnumerable <Foo>>

Этот код генерирует две ошибки времени компиляции:

private void DoSomething()
{
    List<List<Foo>> myFoos = GetFoos();

    UseFoos(myFoos);
}

private void UseFoos(IEnumerable<IEnumerable<Foo>>)
{

}

The best overloaded method match for 'NameSpace.Class.UseFoos(System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<Foo>>)' has some invalid arguments

и

Argument 1: cannot convert from 'System.Collections.Generic.List<System.Collections.Generic.List<Foo>>' to 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<Foo>>'

Листинг IEnumberable<List<Foo>> не является проблемой. Что отличает листинг внутреннего компонента List от того, что он терпит неудачу?

Ответ 1

EDIT: Я только что понял, что на самом деле я не ответил на вопрос о том, как обойти ограничение. К счастью, это довольно просто:

UseFoos(myFoos.Cast<IEnumerable<Foo>>());

Этот код отлично компилируется (когда вы указали имя UseFoos имя) в С# 4, в котором введена общая ковариация и контравариантность для интерфейсов и делегатов.

В качестве более простого примера это работает в С# 4, но не в С# 3:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

Обратите внимание, что даже в С# 4 классы не являются инвариантными, поэтому это не сработает:

// This won't work
List<string> strings = new List<string>();
List<object> objects = strings;

... и даже для интерфейсов он поддерживается только тогда, когда он безопасен:

// This won't work either
IList<string> strings = new List<string>();
IList<object> objects = strings;

Интерфейс (или делегат) должен объявить дисперсию самого параметра типа, поэтому, если вы посмотрите документацию .NET 4 для IEnumerable<T> вы увидите, что он объявлен как

public interface IEnumerable<out T>

где out объявляет ковариацию в T.

Эрик Липперт имеет гораздо больше об этом в своей категории блога ковариации и контравариантности.