Недавно, во время разбора кода одной программы я заметил метод, который выглядел примерно так:
Метод принимает дженерик-перечисление и проверяет, пустое ли оно. Видите ли вы тут потенциальную проблему? Я намекну, проблема в этой строчке:
И в чем же тут проблема? Проблема в том, что эта строчка может оказаться очень неэффективной.
Если вызвать наш метод, передав ему перечисление, которое не реализует ICollection<T> (например, IQueryable результат запроса к Entity Framework или LINQ to SQL), метод Count() будет перебирать всю коллекцию ради выполнения этой проверки.
В случае, когда перечисление реализует ICollection<T>, все в порядке. Метод Count() оптимизирован для таких случаев и проверит свойство Count.
Если говорить человеческим языком, то наша строчка отвечает на вопрос «Количество в перечислении равно нулю?». Но это вовсе не то, что нас интересует. На самом деле нам нужно ответить на вопрос «Есть ли в перечислении хотя бы один элемент?».
Если подойти к задаче таким образом, то решение станет очевидным: использовать расширение Any из пространства имен System.Linq.
Красота такого метода в том, что ему достаточно вызвать MoveNext интерфейса IEnumerable только один раз! У вас может быть бесконечно большое перечисление, но Any вернет результат немедленно.
Еще лучше было бы, поскольку такая проверка используется постоянно, подумать над реализацией своего простого метода-расширения:
Теперь, с таким методом, исходный код выглядит еще проще:
С таким методом-расширением в своем инструментарии, вы никогда снова не проверите пустоту перечисления неэффективно.
public void Foo<T>(IEnumerable<T> items)
{
if(items == null || items.Count() == 0)
{
// Оповестить о пустом перечислении
}
}
Метод принимает дженерик-перечисление и проверяет, пустое ли оно. Видите ли вы тут потенциальную проблему? Я намекну, проблема в этой строчке:
items.Count() == 0
И в чем же тут проблема? Проблема в том, что эта строчка может оказаться очень неэффективной.
Если вызвать наш метод, передав ему перечисление, которое не реализует ICollection<T> (например, IQueryable результат запроса к Entity Framework или LINQ to SQL), метод Count() будет перебирать всю коллекцию ради выполнения этой проверки.
В случае, когда перечисление реализует ICollection<T>, все в порядке. Метод Count() оптимизирован для таких случаев и проверит свойство Count.
Если говорить человеческим языком, то наша строчка отвечает на вопрос «Количество в перечислении равно нулю?». Но это вовсе не то, что нас интересует. На самом деле нам нужно ответить на вопрос «Есть ли в перечислении хотя бы один элемент?».
Если подойти к задаче таким образом, то решение станет очевидным: использовать расширение Any из пространства имен System.Linq.
public void Foo<T>(IEnumerable<T> items)
{
if(items == null || !items.Any())
{
// Оповестить о пустом перечислении
}
}
Красота такого метода в том, что ему достаточно вызвать MoveNext интерфейса IEnumerable только один раз! У вас может быть бесконечно большое перечисление, но Any вернет результат немедленно.
Еще лучше было бы, поскольку такая проверка используется постоянно, подумать над реализацией своего простого метода-расширения:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> items)
{
return items == null || !items.Any();
}
Теперь, с таким методом, исходный код выглядит еще проще:
public void Foo<T>(IEnumerable<T> items)
{
if(items.IsNullOrEmpty())
{
// Оповестить о пустом перечислении
}
}
С таким методом-расширением в своем инструментарии, вы никогда снова не проверите пустоту перечисления неэффективно.