Если не использовать лямбда-выражения

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

Используем ли мы это чрезмерное использование и рассматриваем влияние производительности на использование лямбда-выражений?

Я нашел несколько статей, в которых рассматривается влияние производительности lambda vs anonymous delegates vs for/foreach на разные результаты

Какими должны быть критерии оценки при выборе подходящего решения? За исключением очевидной причины, что он более сжатый код и читается при использовании лямбда.

Ответ 1

Несмотря на то, что я сосредоточусь на первом пункте, я начинаю с того, что даю 2 цента на весь выпуск производительности. Если различия не значительны или интенсивность использования, обычно я не беспокоюсь о микросекундах, которые при добавлении не представляют никакой видимой разницы для пользователя. Я подчеркиваю, что мне все равно, если рассматривать неинтенсивные вызываемые методы. Там, где у меня есть особые соображения по производительности, я сам разрабатываю приложение. Я забочусь о кешировании, об использовании потоков, о умных способах вызова методов (делать несколько звонков или пытаться сделать только один вызов), подключать ли соединения или нет и т.д. И т.д. На самом деле я обычно сосредоточиться на сырой производительности, но на масштабируемости. Меня не волнует, если он работает лучше на крошечный кусочек наносекунды для одного пользователя, но мне очень нравится иметь возможность загружать систему с большим количеством одновременных пользователей, не замечая влияния.

Сказав это, вот мое мнение о пункте 1. Я люблю анонимные методы. Они придают мне большую гибкость и элегантность кода. Другая замечательная особенность анонимных методов заключается в том, что они позволяют мне напрямую использовать локальные переменные из метода контейнера (с точки зрения С#, а не с точки зрения ИЛ, конечно). Они избавляют меня от загрузки кода. Когда я использую анонимные методы? Evey за один раз часть кода, в которой я нуждаюсь, не нужна в другом месте. Если он используется в двух разных местах, мне не нравится copy-paste в качестве метода повторного использования, поэтому я использую простой делегат. Итак, как и Shoosh ответил, неплохо иметь дублирование кода. В теории нет различий в производительности, поскольку анонимы - это трюки С#, а не вещи IL.

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

public static void DoSomethingMethod(string[] names, Func<string, bool> myExpression)
{
    Console.WriteLine("Lambda used to represent an anonymous method");
    foreach (var item in names)
    {
        if (myExpression(item))
            Console.WriteLine("Found {0}", item);
    }
}

Он получает массив строк и для каждого из них, он будет вызывать метод, переданный ему. Если этот метод возвращает true, он скажет "Найдено...". Вы можете вызвать этот метод следующим образом:

string[] names = {"Alice", "Bob", "Charles"};
DoSomethingMethod(names, delegate(string p) { return p == "Alice"; });

Но вы также можете называть его следующим образом:

DoSomethingMethod(names, p => p == "Alice");

В IL нет никакой разницы между тем, что тот, кто использует выражение Lambda, намного читаем. Еще раз, влияние производительности не влияет, поскольку все это трюки компилятора С# (а не трюки компилятора JIT). Так же, как я не чувствовал, что мы злоупотребляем анонимными методами, я не чувствую, что мы злоупотребляем выражениями лямбда для представления анонимных методов. Конечно, та же логика применяется к повторному коду: не делайте лямбда, используйте постоянных делегатов. Существуют и другие ограничения, которые приводят вас к анонимным методам или простым делегатам, например out или ref argument.

Другие приятные вещи в выражениях Lambda - то, что тот же самый синтаксис не должен представлять анонимный метод. Лямбда-выражения также могут представлять... вы догадались, выражения. Возьмем следующий пример:

public static void DoSomethingExpression(string[] names, System.Linq.Expressions.Expression<Func<string, bool>> myExpression)
{
    Console.WriteLine("Lambda used to represent an expression");
    BinaryExpression bExpr = myExpression.Body as BinaryExpression;
    if (bExpr == null)
        return;
    Console.WriteLine("It is a binary expression");
    Console.WriteLine("The node type is {0}", bExpr.NodeType.ToString());
    Console.WriteLine("The left side is {0}", bExpr.Left.NodeType.ToString());
    Console.WriteLine("The right side is {0}", bExpr.Right.NodeType.ToString());
    if (bExpr.Right.NodeType == ExpressionType.Constant)
    {
        ConstantExpression right = (ConstantExpression)bExpr.Right;
        Console.WriteLine("The value of the right side is {0}", right.Value.ToString());
    }
 }

Обратите внимание на немного отличающуюся подпись. Второй параметр получает выражение, а не делегат. Способ вызова этого метода:

DoSomethingExpression(names, p => p == "Alice");

Это точно так же, как вызов, который мы создали при создании анонимного метода с помощью лямбда. Разница здесь в том, что мы не создаем анонимный метод, а создаем дерево выражений. Именно из-за этих деревьев выражений мы можем затем перевести лямбда-выражения в SQL, что, например, делает Linq 2 SQL, вместо того, чтобы запускать материал в движке для каждого предложения, например Where, Select и т.д. Хорошая вещь что синтаксис вызова одинаковый, независимо от того, создаете ли вы анонимный метод или отправляете выражение.

Ответ 2

Мой ответ не будет популярным.

Я считаю, что Lambda на 99% всегда лучший выбор по трем причинам.

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

Во-вторых, Lamdas (и др.) являются современным синтаксисом - и завтра они станут более обычным явлением, чем они есть сегодня. Код проекта должен вытекать из текущих и возникающих соглашений.

В-третьих, писать код "старомодный путь" может показаться вам проще, но компилятору это не так просто. Это важно, унаследованные подходы имеют мало возможностей для совершенствования по мере того, как компилятор обновляется. Lambdas (и др.), Которые полагаются на компилятор для их расширения, могут выиграть, поскольку компилятор справляется с ними лучше с течением времени.

Подводя итог:

  • Разработчики могут справиться с этим.
  • Все это делают
  • Там будущий потенциал

Опять же, я знаю, что это не будет популярным ответом. И поверьте мне, что "Простой лучший" - тоже моя мантра. Техническое обслуживание является важным аспектом для любого источника. Я понял. Но я думаю, что мы омрачаем реальность некоторыми эмпирическими эмпирическими правилами.

//Джерри

Ответ 3

Дублирование кода.
Если вы обнаруживаете, что используете одну и ту же анонимную функцию несколько раз, она не должна быть такой.

Ответ 4

Хорошо, когда мы говорим о делегировании делегата, не должно быть никакой разницы между лямбдами и анонимными методами - они одинаковы, только с разным синтаксисом. Именованные методы (используемые как делегаты) также идентичны с точки зрения времени выполнения. Таким образом, разница заключается в использовании делегатов, а также встроенного кода - i.e.

list.ForEach(s=>s.Foo());
// vs.
foreach(var s in list) { s.Foo(); }

(где я ожидал бы, что последнее будет быстрее)

И в равной степени, если вы говорите о чем-либо, кроме объектов в памяти, lambdas - один из ваших самых мощных инструментов в плане поддержания проверки типов (а не для разбора строк).

Конечно, бывают случаи, когда простой foreach с кодом будет быстрее, чем версия LINQ, так как будет меньше вызовов, и вызывает стоимость небольшого, но измеримого времени. Однако во многих случаях код просто не является узким местом, а более простой код (особенно для группировки и т.д.) Стоит гораздо больше, чем несколько наносекунд.

Обратите внимание, что в .NET 4.0 есть дополнительные Expression узлы для таких вещей, как циклы, запятые и т.д. Язык не поддерживайте их, но время выполнения. Я упоминаю об этом только для полноты: я, конечно, не говорю, что вы должны использовать конструкцию Expression для руководства, где foreach будет делать!

Ответ 5

Я бы сказал, что различия в производительности обычно такие маленькие (и в случае циклов, очевидно, если вы посмотрите на результаты второй статьи (кстати, у Jon Skeet есть аналогичная статья здесь)), что вы почти никогда не выбираете решение только по соображениям производительности, если только вы не пишете часть программного обеспечения, где производительность абсолютно является нефункциональным требованием номер один и вам действительно нужно делать микрооптимизации.

Когда выбирать что? Думаю, это зависит от ситуации, но и от человека. Как пример, некоторые люди пересылают List.Foreach через обычный цикл foreach. Я лично предпочитаю последнее, поскольку он обычно более читабельен, но кто я такой, чтобы возражать против этого?

Ответ 6

Правила большого пальца:

  • Напишите свой код как естественный и читаемый.
  • Избегайте дублирования кода (выражения лямбда могут потребовать немного дополнительного усердия).
  • Оптимизируйте только тогда, когда есть проблема, и только с данными, чтобы создать резервную копию этой проблемы.

Ответ 7

Всякий раз, когда лямбда просто передает свои аргументы непосредственно другой функции. Не создавайте приложение для лямбды для функций.

Пример:

var coll = new ObservableCollection<int>();
myInts.ForEach(x => coll.Add(x))

Лучше, чем:

var coll = new ObservableCollection<int>();
myInts.ForEach(coll.Add)

Основным исключением является то, что вывод типа С# невозможен по какой-либо причине (и есть много раз, что это правда).

Ответ 9

Лямбда-выражения - классные. В более старом delegate синтаксисе у них есть несколько преимуществ, таких как: они могут быть преобразованы в анонимную функцию или деревья выражений, типы параметров выводятся из декларации, они более чистые и более сжатые и т.д. я не вижу реального значения, чтобы не использовать лямбда-выражение, когда вы нуждаетесь в анонимной функции. Одно не очень большое преимущество, чем раньше, так это то, что вы можете полностью опустить объявление параметра, если они не используются. Как

Action<int> a = delegate { }; //takes one argument, but no argument specified

Это полезно, когда вам нужно объявить пустой делегат, который ничего не делает, но это не является достаточной причиной для того, чтобы не использовать lambdas.

Lambdas позволяет писать быстрые анонимные методы. Теперь это делает лямбды бессмысленными везде, где анонимные методы становятся бессмысленными, т.е. Когда названные методы имеют больше смысла. По названным методам анонимные методы могут быть невыгодными (а не лямбда-выражения как таковые, но поскольку в наши дни лямбды широко представляют собой анонимные методы, это актуально):

  • потому что он имеет тенденцию приводить к дублированию логики (часто это трудно, повторное использование)

  • когда нет необходимости писать в один, например:

    //this is unnecessary 
    Func<string, int> f = x => int.Parse(x);
    
    //this is enough
    Func<string, int> f = int.Parse;
    
  • поскольку запись анонимного блока итератора невозможна.

    Func<IEnumerable<int>> f = () => { yield return 0; }; //impossible
    
  • поскольку рекурсивным лямбдам требуется еще одна строка причудливости, например

    Func<int, int> f = null;
    f = x => (x <= 1) ? 1 : x * f(x - 1);
    
  • а, так как отражение своего рода Мессье, но это спорный вопрос, не так ли?

Помимо пункта 3, остальные не являются вескими причинами не использовать лямбда.

Также см. этот поток о том, что невыгодно для делегатов Func/Action, так как часто они используются вместе с лямбда-выражениями.