IEnumerable не гарантирует, что повторное перечисление даст тот же результат. Фактически, довольно легко создать пример, в котором myEnumerable.First ()
возвращает разные значения при двойном выполнении:
class A {
public A(string value) { Value = value; }
public string Value {get; set; }
}
static IEnumerable getFixedIEnumerable() {
return new A[] { new A("Hello"), new A("World") };
}
static IEnumerable getDynamicIEnumerable() {
yield return new A("Hello");
yield return new A("World");
}
static void Main(string[] args)
{
IEnumerable fix = getFixedIEnumerable();
IEnumerable dyn = getDynamicIEnumerable();
Console.WriteLine(fix.First() == fix.First()); // true
Console.WriteLine(dyn.First() == dyn.First()); // false
}
Это не просто академический пример: использование популярного из ... in ... select new A (...)
создаст именно такую ситуацию. Это может привести к неожиданному поведению:
fix.First().Value = "NEW";
Console.WriteLine(fix.First().Value); // prints NEW
dyn.First().Value = "NEW";
Console.WriteLine(dyn.First().Value); // prints Hello
Я понимаю, почему это происходит. Я также знаю, что это можно исправить, выполнив ToList ()
в Enumerable или переопределив ==
для класса A
. Это не мой вопрос.
Возникает вопрос: когда вы пишете метод, который принимает произвольный IEnumerable, и вам нужно свойство, согласно которому последовательность вычисляется только один раз (а затем ссылки становятся «фиксированными»), каков канонический способ сделать это? ToList ()
, похоже, используется в основном, но если источник уже исправлен (например, если источник является массивом), ссылки копируются в список (без необходимости, поскольку все, что мне нужно, это фиксированная собственность). Есть ли что-то более подходящее или ToList ()
«каноническое» решение этой проблемы?