Подтвердить что ты не робот

Расширение LINQ для принятия нулевых переменных

Во время работы с расширениями Linq нормальный вид выглядит следующим образом:

IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;
if (enumerable != null)
{
    sum = enumerable.Sum();
}

Чтобы повысить качество кода, я написал следующий метод расширения, который проверяет нулевые числа и прерывает выполнение linq.

public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
    if (enumerable == null) yield break;
    foreach (var item in enumerable)
    {
        yield return item;
    }
}

Итак, я могу реорганизовать код таким образом:

var sum = GetEnumerable().IgnoreIfEmpty().Sum();

Мои вопросы сейчас:

  • Какие штрафы связаны с моим методом расширения во время выполнения?
  • Является ли хорошей практикой расширение linq таким образом?

Update: Моя целевая структура: 3.5

4b9b3361

Ответ 1

  • У вас будет накладные расходы на вызов метода, это будет незначительно, если вы не запускаете его в узком цикле или сценарии критики производительности. Это только тень по сравнению с чем-то вроде вызова базы данных или записи в файловую систему. Обратите внимание, что метод, вероятно, не будет встроен, поскольку он является перечислителем.
  • Все о читаемости/ремонтопригодности. Что я ожидаю, когда увижу GetEnumerable().IgnoreIfEmpty().Sum();? В этом случае это имеет смысл.

Обратите внимание, что с С# 6 мы можем использовать следующий синтаксис: GetEnumerable()?.Sum(), который возвращает int?. Вы можете написать GetEnumerable()?.Sum() ?? 0 или GetEnumerable()?.Sum().GetValueOrDefault(), чтобы получить ненулевое целое число, которое будет по умолчанию равно нулю.

Если вы действительно заинтересованы в производительности, вы также можете немного реорганизовать свой метод, чтобы он не был перечислителем. Это может увеличить вероятность вложения, хотя я не имею понятия о "тайной" логике JIT-компилятора:

public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
    if (enumerable == null) return Enumerable.Empty<T>();
    return enumerable;
}

В общем, о расширении Linq, я думаю, что это прекрасно, если код имеет смысл. MSDN даже имеет статью об этом. Если вы посмотрите на стандартные методы Where, Select в Linq и забудьте об оптимизации производительности, которые у них есть, все методы в основном однострочные методы.

Ответ 2

Какие штрафы связаны с моим методом расширения во время выполнения?

Ваш метод расширения преобразуется в состояние-машина, поэтому есть минимальные накладные расходы, но это не должно быть заметно.

Хорошо ли продлить linq таким образом?

В вашем вопросе вы указываете:

При работе с расширениями Linq это нормально, чтобы увидеть код, подобный этому (вставьте здесь перечисляемую нулевую проверку)

И я прошу отличить. В распространенной практике не возвращает значение null, где ожидается IEnumerable<T>. Большинство случаев должны возвращать пустую коллекцию (или IEnumerable), оставляя null для исключительной, потому что null не пуст. Это сделает ваш метод полностью избыточным. Используйте Enumerable.Empty<T>, если необходимо.

Ответ 3

Вы можете пропустить дополнительный метод расширения и использовать оператор null coalescing - это то, для чего он нужен, и одноразовая проверка на тождественность должна быть намного более эффективной чем другой конечный автомат:

IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;

sum = (enumerable ?? Enumerable.Empty<int>()).Sum();

Ответ 4

В большинстве случаев мы пишем много кода только потому, что мы очарованы красотой нашего творения - не потому, что нам это действительно нужно - и затем мы называем это абстракцией, возможностью повторного использования, расширяемости, и т.д.

Является ли этот необработанный фрагмент менее читаемым или менее растяжимым или менее повторным или медленным:

var sum = GetEnumerable().Where(a => a != null).Sum(); 

Чем меньше кода вы пишете, тем меньше кода, который вы тестируете, прост. BTW - хорошо писать методы расширения, если вы можете это обосновать.