Возврат IList‹IList‹T››

У меня есть метод, который строит списки списков. Я хотел бы, чтобы возвращаемый тип использовал общий интерфейс IList‹>, чтобы уменьшить связь с конкретным типом List‹> ниже по течению. Однако компилятор борется с преобразованием типов.

public IList<IList<T>> Foo<T>()
    {
        return new List<List<T>>();
    } 

Почему это не работает, когда это работает:

public IList<T> Foo<T>()
    {
        return new List<T>();
    } 

Каков самый элегантный выход из этого беспорядка?


person Jason Suárez    schedule 15.11.2011    source источник
comment
Какую версию .NET вы используете?   -  person Oded    schedule 16.11.2011


Ответы (2)


Просто сделайте это:

return new List<IList<T>>();

Поскольку List<T> реализует IList<T>, вы сможете добавить к результату любой тип IList<T>. Например:

Foo<int>().Add(new List<int>());

Что касается почему, это вопрос ковариантности/контравариантности (я всегда путаю их). По сути, если вы говорите, что возвращаете IList<IList<T>>, то кто-то должен иметь возможность добавить любой вид IList<T> к результату:

Foo<int>().Add(new []{1}); // arrays implement `IList`

Очевидно, что если бы вы вернули List<List<T>>, то приведенная выше строка кода не сработала бы: вы попытались бы добавить T[] в коллекцию List<T>. Таким образом, несмотря на то, что List<T> является IList<T>, List<List<T>> не является IList<IList<T>>.

Примечание. Следующая часть относится только к .NET 4 и более поздним версиям, поскольку это была первая версия, поддерживающая ковариантность и контравариантность в интерфейсах.

С другой стороны, если вы знаете, что человек, использующий ваш результат, не планирует ничего добавлять в список, а только пытается выполнить итерацию по нему, вы можете изменить тип возвращаемого значения на IEnumerable<IList<T>>, и тогда было бы совершенно нормально вернуть List<List<T>>. Почему? Поскольку интерфейс IEnumerable<T> является ковариантным:

public interface IEnumerable<out T>

Это "out" сообщает компилятору, что этот интерфейс будет иметь только методы, которые возвращают значения, относящиеся к T (например, GetEnumerator), и не будет методов, которые принимают что-то, связанное с T, в качестве параметра ( как Add).

person StriplingWarrior    schedule 15.11.2011
comment
Небольшое замечание: LinkedList<> не реализует IList<>. - person phoog; 16.11.2011
comment
Спасибо за совет. Сейчас я создаю IEnumerable‹IList‹T›› и использую Linq .ToList() для возврата. - person Jason Suárez; 17.11.2011