I am looking at the new implementations in C# 7.0 and I find it interesting that they have implemented local functions but I cannot imagine a scenario where a local function would be preferred over a lambda expression, and what is the difference between the two.
I do understand that lambdas are anonymous
functions meanwhile local functions are not, but I can't figure out a real world scenario, where local function has advantages over lambda expressions
Any example would be much appreciated. Thanks.
This was explained by Mads Torgersen in C# Design Meeting Notes where local functions were first discussed:
You want a helper function. You are only using it from within a single function, and it likely uses variables and type parameters that are in scope in that containing function. On the other hand, unlike a lambda you don't need it as a first class object, so you don't care to give it a delegate type and allocate an actual delegate object. Also you may want it to be recursive or generic, or to implement it as an iterator.
To expand on it some more, the advantages are:
Performance.
When creating a lambda, a delegate has to be created, which is an unnecessary allocation in this case. Local functions are really just functions, no delegates are necessary.
Also, local functions are more efficient with capturing local variables: lambdas usually capture variables into a class, while local functions can use a struct (passed using ref
), which again avoids an allocation.
This also means calling local functions is cheaper and they can be inlined, possibly increasing performance even further.
Local functions can be recursive.
Lambdas can be recursive too, but it requires awkward code, where you first assign null
to a delegate variable and then the lambda. Local functions can naturally be recursive (including mutually recursive).
Local functions can be generic.
Lambdas cannot be generic, since they have to be assigned to a variable with a concrete type (that type can use generic variables from the outer scope, but that's not the same thing).
Local functions can be implemented as an iterator.
Lambdas cannot use the yield return
(and yield break
) keyword to implement IEnumerable<T>
-returning function. Local functions can.
Local functions look better.
This is not mentioned in the above quote and might be just my personal bias, but I think that normal function syntax looks better than assigning a lambda to a delegate variable. Local functions are also more succinct.
Compare:
int add(int x, int y) => x + y;
Func<int, int, int> add = (x, y) => x + y;
In addition to svick's great answer there is one more advantage to local functions:
They can be defined anywhere in the function, even after the return
statement.
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;
}
I use inline functions to avoid garbage collection pressure specially when dealing with longer running methods. Say one would like to get 2 years or market data for a given ticker symbol. Also, one can pack a lot of functionality and business logic if one needs to.
what one does is open a socket connection to the server and loop over the data binding an event to a event. One can think of it the same way as a class is designed, only one is not writing helper methods all over the place that are really only working for one pice of functionality. below is some sample of how this might look like, please note that i am using variables and the "helper" methods are below the finally. In the Finally I nicely remove the event handlers, if my Exchange class would be external/injected i would not have any pending event handler registrated
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;
}
}
You can see the advantages as mentioned below, here you can see a sample implementation. Hope that helps explaining the benefits.