Локальная функция vs Lambda С# 7.0
Я рассматриваю новые реализации в С# 7.0, и мне интересно, что они реализовали локальные функции, но я не могу представить сценарий, в котором локальная функция будет предпочтительнее выражения лямбда, и в чем разница между ними.
Я понимаю, что лямбды - это anonymous
функции, а локальные функции - нет, но я не могу понять сценарий реального мира, где локальная функция имеет преимущества перед лямбда-выражениями
Любой пример будет высоко оценен. Благодарю.
Ответы
Ответ 1
Это объяснялось Mads Torgersen в С# Design Meeting Notes, где локальные функции были впервые обсуждены:
Вам нужна вспомогательная функция. Вы используете его только из одной функции и, вероятно, используете переменные и параметры типа, которые находятся в области видимости этой функции. С другой стороны, в отличие от лямбда вам это не нужно как объект первого класса, поэтому вам не нужно указывать тип делегирования и распределять фактический объект-делегат. Также вы можете захотеть, чтобы он был рекурсивным или универсальным или реализовал его как итератор.
Чтобы расширить его, преимущества:
-
Производительность.
При создании лямбда должен быть создан делегат, который в этом случае является ненужным. Локальные функции - это действительно просто функции, не требуются делегаты.
Кроме того, локальные функции более эффективны при захвате локальных переменных: lambdas обычно захватывает переменные в классе, тогда как локальные функции могут использовать структуру (переданную с использованием ref
), что опять позволяет избежать выделения.
Это также означает, что вызывать локальные функции дешевле, и они могут быть встроены, что, возможно, еще больше повышает производительность.
-
Локальные функции могут быть рекурсивными.
Lambdas также может быть рекурсивным, но для этого требуется неловкий код, где вы сначала назначаете null
переменной-делегату, а затем лямбда. Локальные функции, естественно, могут быть рекурсивными (в том числе взаимно рекурсивными).
-
Локальные функции могут быть общими.
Lambdas не может быть общим, поскольку они должны быть назначены переменной с конкретным типом (этот тип может использовать общие переменные из внешней области, но это не одно и то же).
-
Локальные функции могут быть реализованы как итератор.
Lambdas не может использовать ключевое слово yield return
(и yield 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;
}
}
Вы можете увидеть преимущества, как указано ниже, здесь вы можете увидеть пример реализации. Надеюсь, что это поможет объяснить преимущества.