Локальная функция vs Lambda С# 7.0

Я рассматриваю новые реализации в С# 7.0, и мне интересно, что они реализовали локальные функции, но я не могу представить сценарий, в котором локальная функция будет предпочтительнее выражения лямбда, и в чем разница между ними.

Я понимаю, что лямбды - это anonymous функции, а локальные функции - нет, но я не могу понять сценарий реального мира, где локальная функция имеет преимущества перед лямбда-выражениями

Любой пример будет высоко оценен. Благодарю.

Ответ 1

Это объяснялось Mads Torgersen в С# Design Meeting Notes, где локальные функции были впервые обсуждены:

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

Чтобы расширить его, преимущества:

  • Производительность.

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

    Кроме того, локальные функции более эффективны при захвате локальных переменных: lambdas обычно захватывает переменные в классе, тогда как локальные функции могут использовать структуру (переданную с использованием ref), что опять позволяет избежать выделения.

    Это также означает, что вызывать локальные функции дешевле, и они могут быть встроены, что, возможно, еще больше повышает производительность.

  • Локальные функции могут быть рекурсивными.

    Lambdas также может быть рекурсивным, но для этого требуется неловкий код, где вы сначала назначаете null переменной-делегату, а затем лямбда. Локальные функции, естественно, могут быть рекурсивными (в том числе взаимно рекурсивными).

  • Локальные функции могут быть общими.

    Lambdas не может быть общим, поскольку они должны быть назначены переменной с конкретным типом (этот тип может использовать общие переменные из внешней области, но это не одно и то же).

  • Локальные функции могут быть реализованы как итератор.

    Lambdas не может использовать ключевое слово yield returnyield break) для реализации функции IEnumerable<T> -returning. Локальные функции могут.

  • Локальные функции выглядят лучше.

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

    Для сравнения:

    int add(int x, int y) => x + y;
    Func<int, int, int> add = (x, y) => x + y;
    

Ответ 2

В дополнение к svick отличный ответ есть еще одно преимущество для локальных функций:
Они могут быть определены где угодно в функции, даже после инструкции return.

public double DoMath(double a, double b)
{
    var resultA = f(a);
    var resultB = f(b);
    return resultA + resultB;

    double f(double x) => 5 * x + 3;
}

Ответ 3

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

public class Foo // the class under test
{ 
    public int GetResult() 
    { 
        return 100 + GetLocal(); 
        int GetLocal () 
        { 
            return 42; 
        } 
    } 
}

А вот как выглядит тест:

[TestClass] 
public class MockLocalFunctions 
{ 
    [TestMethod] 
    public void BasicUsage() 
    { 
        //Arrange 
        var foo = Mock.Create<Foo>(Behavior.CallOriginal); 
        Mock.Local.Function.Arrange<int>(foo, "GetResult", "GetLocal").DoNothing(); 

        //Act 
        var result = foo. GetResult(); 

        //Assert 
        Assert.AreEqual(100, result); 
    } 
} 

Вот ссылка на документацию JustMock documentation.

Отказ от ответственности. Я являюсь одним из разработчиков, отвечающих за JustMock.

Ответ 4

Я использую встроенные функции, чтобы избежать избыточного сбора мусора, особенно при работе с более длинными режимами работы. Скажем, вы хотели бы получить 2 года или рыночные данные для данного символа тикера. Кроме того, можно собрать много функциональности и бизнес-логики, если нужно.

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

void List<HistoricalData> RequestData(Ticker ticker, TimeSpan timeout)
{
    var socket= new Exchange(ticker);
    bool done=false;
    socket.OnData += _onData;
    socket.OnDone += _onDone;
    var request= NextRequestNr();
    var result = new List<HistoricalData>();
    var start= DateTime.Now;
    socket.RequestHistoricalData(requestId:request:days:1);
    try
    {
      while(!done)
      {   //stop when take to long….
        if((DateTime.Now-start)>timeout)
           break;
      }
      return result;

    }finally
    {
        socket.OnData-=_onData;
        socket.OnDone-= _onDone;
    }


   void _OnData(object sender, HistoricalData data)
   {
       _result.Add(data);
   }
   void _onDone(object sender, EndEventArgs args)
   {
      if(args.ReqId==request )
         done=true;
   } 
}

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