Что приводит к тому, что этот список передается по ссылке при вызове в одном направлении, но по значению другой?
Я делал простой тест для запуска метода проверки и сталкивался с этой странной ситуацией.
public IEnumerable<int> ints (List<int> l)
{
if(false)yield return 6;
l.Add(4);
}
void Main()
{
var a = new List<int>();
var b = new List<int>();
for( int i = 0; i < 4; i++ ){
a.Add(i);
b.Add(i);
}
a.AddRange(ints(a));
ints(b);
Console.WriteLine(a);
Console.WriteLine(b);
}
Как только этот код будет запущен, a
будет содержать [0,1,2,3,4]
. Однако b
будет содержать [0,1,2,3]
. Почему вызов метода в качестве аргумента в AddRange
разрешил передачу списка по ссылке? Или, если этого не произошло, что же?
Ответы
Ответ 1
ints(b)
вызов не перечисляет IEnumerable
, поэтому код никогда не достигает строки l.Add(4)
, в отличие от случая AddRange
, который перечисляет все элементы, чтобы добавить их в список.
Чтобы увидеть, что он вызвал для случая b
, перечислить результат вручную:
ints(b).ToList();
IEnumerable<T>
, реализованный через функции, не выполняет тело функции до начала перечисления - код фактически преобразуется компилятором в класс с состояниями для поддержки истинной ленивой оценки перечислимого (подробности могут быть найдены в нескольких статьях, т.е. Iterator Pattern demystified - ссылка предоставлена Тимом Шмельтером).