Resharper: возможно многократное перечисление IEnumerable

Я использую новую версию Resharper 6. В нескольких местах моего кода она подчеркнула некоторый текст и предупредила меня, что может быть возможно множественное перечисление IEnumerable.

Я понимаю, что это значит, и, при необходимости, принял совет, но в некоторых случаях я не уверен, что это действительно большая проблема.

Как в следующем коде:

var properties = Context.ObjectStateManager.GetObjectStateEntry(this).GetModifiedProperties();
if (properties.Contains("Property1") || properties.Contains("Property2") || properties.Contains("Property3")) {
    ...
}

Он подчеркивает каждое упоминание properties во второй строке, предупреждая, что я перечисляю этот IEnumerable несколько раз.

Если я добавлю .ToList() в конец строки 1 (поворот properties от IEnumerable<string> до List<string>), предупреждения исчезнут.

Но, конечно, если я преобразую его в список, то он перечислит весь IEnumerable для создания списка в первую очередь, а затем перечислит список, необходимый для поиска свойств (т.е. 1 полное перечисление и 3 частичных перечисления). Если в моем исходном коде он делает только 3 частичных перечисления.

Неужели я ошибаюсь? Какой лучший метод здесь?

Ответ 1

Я точно не знаю, что ваш properties на самом деле здесь, но если он по существу представляет запрос на немедленную базу данных, то ваш оператор if выполнит три запроса.

Я подозреваю, что было бы лучше сделать:

string[] propertiesToFind = { "Property1", "Property2", "Property3" };
if (properties.Any(x => propertiesToFind.Contains(x))
{
     ...
}

Это будет логически только переходить по последовательности один раз - и если есть запрос к базе данных, он вполне может просто использовать предложение SQL "IN", чтобы сделать все это в базе данных в одном запросе.

Ответ 2

Если вы вызываете Contains() в IEnumerable, он будет вызывать метод расширения, который будет просто перебирать элементы, чтобы найти его. IList имеет реальную реализацию для Contains(), которая, вероятно, более эффективна, чем регулярная итерация через значения (у нее может быть дерево поиска с хешами?), поэтому она не предупреждает с помощью IList.

Поскольку метод расширения будет знать только, что он IEnumerable, он, вероятно, не может использовать какие-либо встроенные методы для Contains(), хотя в теории было бы возможно идентифицировать известные типы и соответственно их применять использовать их.