Я пытаюсь вернуть List<List<string>> в методе, который имеет тип возвращаемого значения IList<IList<string>>:
IList<IList<string>> Foo()
{
return new List<List<string>>();
}
Однако я получаю ошибку компиляции, в которой говорится, что я не могу преобразовать List<List<string>> в IList<IList<string>>. Я подумал, что, поскольку List<T> реализует IList<T>, я смогу вернуть List<List<string>> там, где ожидается IList<IList<string>>.
Я спросил об этом второго пилота и получил ответы о том, что IList не является ковариантным или контравариантным, но до сих пор не могу понять это. Возвращаемый тип List<IList<string>> действителен, и это меня смущает.
Вместо этого используйте IReadOnlyList<IReadOnlyList<string>>
Общая ковариация интерфейса означает, что мы можем заменить T
типом, который является тем же самым или может быть присвоен T
(в случае иерархии классов, более производной, в случае интерфейсов - класс, реализующий интерфейс)
IList<T>
не является ковариантным. IEnumerable<out T>
есть (обратите внимание на ключевое слово out
)
Например, это будет отлично работать с IEnumerable, поскольку мы можем заменить общий параметр IList<string>
на List<string>
, поскольку последний реализует интерфейс IList<string>
.
IEnumerable<IList<string>> Foo() {
return new List<List<string>>();
}
Однако IList<T>
нужно заменить на точно такой же T
.
Итак, если вам нужно придерживаться своего примера, вы можете изменить его следующим образом:
IList<IList<string>> Foo() {
return new List<IList<string>>();
}
и использовать его так без проблем
var foo = Foo();
foo.Add(new List<string>());
foo[0].Add("bar");
Console.WriteLine(foo[0][0]);
Если бы это было разрешено, то я мог бы сделать
Foo().Add(new ArraySegment<string>(new string[]{}))
и поместить вList<List<string>>
что-нибудь, что не являетсяList<string>
.