IEnumerable не гарантирует, что двойное перечисление даст один и тот же результат. На самом деле, довольно просто создать пример, где myEnumerable.First()
возвращает разные значения при двойном выполнении:
class A {
public A(string value) { Value = value; }
public string Value {get; set; }
}
static IEnumerable<A> getFixedIEnumerable() {
return new A[] { new A("Hello"), new A("World") };
}
static IEnumerable<A> getDynamicIEnumerable() {
yield return new A("Hello");
yield return new A("World");
}
static void Main(string[] args)
{
IEnumerable<A> fix = getFixedIEnumerable();
IEnumerable<A> dyn = getDynamicIEnumerable();
Console.WriteLine(fix.First() == fix.First()); // true
Console.WriteLine(dyn.First() == dyn.First()); // false
}
Это не просто академический пример: использование популярного from ... 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()
, кажется, в основном используется, но если источник уже зафиксирован (например, если источник является массивом), ссылки копируются в список (без необходимости, поскольку все, что мне нужно, это свойство fixed). Есть что-то более подходящее или ToList()
"каноническое" решение для этой проблемы?