This question raises the question of planned innovations in version 7.

In particular, I was interested in the so-called local functions .

The @VladD response has an example of how a local function can be used:

IEnumerable<int> GetOdd(IEnumerable<int> s) { if (s == null) throw new ArgumentNullException(); // обратите внимание, `Inner` без параметров IEnumerable<int> Inner() { foreach (var v in s) if (v % 2 != 0) yield return v; } return Inner(); } 

but it seems to me that this example is slightly strained , in this case it would probably be possible to do without the Inner() function.

I also do not understand the following statement:

Regarding local functions, it seems to me that, on the contrary, often private functions of classes are used as a crutch in the absence of local functions. Often, a helper from a single function that has no value inside a class is taken to a private function. The local function is the more correct path for such functions.

I code in my code using separate methods based on the following rules:

  1. Functionality that can be reused;
  2. Functionalization in a separate method to maintain the harmony of the function;
  3. Other

For example:

 public IEnumerable<string> GetPhonesForNotice(requestId) { var requestState = GetStateOfRequest(int requestId); switch(requestState) { case "Открыта": { return getRecipientsForNewRequest(int requestId); break; } case "Закрыта": { return getRecipientsForClosedRequest(int requestId); break; } } } private IEnumerable<string> getRecipientsForNewRequest(int requestId) { } private IEnumerable<string> getRecipientsForClosedRequest(int requestId) { } 

If I understood correctly, then in the new version of the function language getRecipientsForNewRequest (int requestId) , getRecipientsForClosedRequest (int requestId) can be implemented inside the main function GetPhonesForNotice (int requestId) but I don’t understand what is better / better than the current option?

    2 answers 2

    I think that's how.

    The question is not the technical side of things. (Although there are technical differences, they are given at the end of the answer.) On the technical side, it is possible to organize the same private function to which all parameters are passed explicitly. And if the internal function should have fewer parameters (for example, due to the fact that a specific signature is required), you can construct a closure. All technical issues are solved within the framework of the old possibilities. (Just as it would be possible to do without properties and use a couple of functions, without classes, and pass this into methods explicitly, etc.)

    The fact is that we do not want to contaminate the helper functions of the class, but put them inside what they refer to. This is a complete analogy of the idea that a class should contain everything that is necessary for it to work, and not require "external" control. Similarly, here, the function contains within itself what is needed for work, and private helper methods are needed only where they are not limited to the meaning of one function - that is, where they are meaningful within the whole class .

    With this approach, yes, local variables in some way replace the class fields, so in situations where you previously needed to create an internal class and call methods in it, you can now do the same thing more conveniently with local functions.

    In addition, reducing the binding code like an implicit transfer of variables is always important, so binding the code is an opportunity to make a mistake.


    As another motivating example:

     void SortBy<T>(List<T> list, Func<List, double> expr, bool ascending) { int comparerAscending(T t1, T t2) { return expr(t1).Compare(expr(t2)); } int comparerDescending(T t1, T t2) { return expr(t2).Compare(expr(t1)); } list.Sort(ascending ? comparerAscending : comparerDescending); } 

    Without local functions, you would have to create internal delegates.


    Another scenario in which internal functions can be useful is code generation. If you generate an auxiliary function, you may accidentally get on a name already taken, you do not know what is in the rest of the class. If the auxiliary function is “packaged” into another function, this problem does not occur.


    Addition: Let's consider, besides the logical ones, the differences between local functions and already existing means of the language.

    The difference from a private non-local function is, besides the “hidden” name, that the local function “sees” the variables declared in the enclosing function before it. Thus, there is no need to pass parameters to a local function explicitly , and this means that we can more freely manage its signature. (This may be important, see the SortBy example.)

    The difference between a local function and a similar locally declared delegate with a lambda function is that

    • delegate variable can be redefined, local function name cannot be reassociated
    • lambda functions require a tricky syntax to implement a recursive call, which still breaks when the delegate variable is subsequently changed (Y-combinator does not offer!), local functions do not have problems
    • lambda function cannot be a yield return
    • you cannot declare a generalized lambda function ( Func<T, int> f = t => 1; does not compile for an unknown type T ), there are no problems with local functions
    • performance: delegate means an extra allocation of a delegate type instance; the local function of this defect is deprived, and additional allocation is needed only for the case of a non-trivial closure.

    Literature: C # Design Notes for May 20, 2015

    • What is the difference between local functions and internal delegates in the Sort example? - Grundy
    • @Grundy: Well, firstly, in essence, these are nested functions, and the delegate at this point is only a crutch for their absence in the language. Secondly, the delegate can be reassigned, with the delegate you need dances with a tambourine for recursion, the delegate can not contain a yield return . Well and the delegate is a class wrapper over function, it is less effective. - VladD
    • I think it’s worth adding to the answer, but now there’s no difference at all, with delegates it would even look more compact, something like the list.Sort(ascending ? (a,b)=>a.Compare(b) : (a,b)=>b.Compare(a)); , there is no problem with recursion either - you give the name to the delegate and you can use it, and with the rest, plus the ref / out parameters - Grundy
    • @Grundy: posted. There is a problem with recursion, because the delegate variable can be reassigned. Example: Func<int, int> f = null; f = (n => n == 0 ? 0 : 1 + f(n - 1)); var g = f; if (negative) f = (n => n == 0 ? 0 : -1 + f(n + 1)); g(1); Func<int, int> f = null; f = (n => n == 0 ? 0 : 1 + f(n - 1)); var g = f; if (negative) f = (n => n == 0 ? 0 : -1 + f(n + 1)); g(1); - VladD
    • I did not want to write a separate question. I do not understand about allocation. Many people write that lambda require allocation, but judging by this on stackoverflow.com/questions/38742278/… it is not. Also in the test code where in a cycle a lambda is created and used a trillion times (GC is turned off), no increase in memory consumption is observed and the number of objects in snapshots does not change. Who is wrong? - vitidev

    I found the use for myself in the case when: Suppose the GetPhonesForNotice method has a local variable name , and the same variable will / should be found in the getRecipientsForNewRequest methods, getRecipientsForClosedRequest .

     public Class PhoneBook { public IEnumerable<string> GetPhonesForNotice(requestId) { string name="qqq"; //локальная переменная var requestState = GetStateOfRequest(int requestId); switch(requestState) { case "Открыта": { return getRecipientsForNewRequest(int requestId); break; } case "Закрыта": { return getRecipientsForClosedRequest(int requestId); break; } } } private IEnumerable<string> getRecipientsForNewRequest(int requestId) { //какие-то операции с name } private IEnumerable<string> getRecipientsForClosedRequest(int requestId) { //какие-то операции с name } } 

    In this case, the name variable will have to be moved outside the GetPhonesForNotice method:

     public Class PhoneBook { private name = "qqq"; //вынесли локальную переменную из метода public IEnumerable<string> GetPhonesForNotice(requestId) { var requestState = GetStateOfRequest(int requestId); //какие-то операции с name switch(requestState) { case "Открыта": { return getRecipientsForNewRequest(int requestId); break; } case "Закрыта": { return getRecipientsForClosedRequest(int requestId); break; } } } private IEnumerable<string> getRecipientsForNewRequest(int requestId) { //какие-то операции с name } private IEnumerable<string> getRecipientsForClosedRequest(int requestId) { //какие-то операции с name } } 

    or pass it as a parameter to getRecipientsForNewRequest , getRecipientsForClosedRequest . Using local functions would have to do neither.

    As a conclusion, the benefit of local functions is that you do not have to spawn private variables for several methods and not clog private parameters with input parameters.

    • yes I agree about variables, if I used local then getRecipientsForNewRequest() & getRecipientsForClosedRequest() would be without parameters, just in this case, the main function can be very large - Bald
    • @Bald, I believe that with a strong proliferation of the main method due to local functions, many still make a separate class, or some resharper will offer to do it for us, because in spite of some conveniences, there is still a large number of local monsters and business logic between / under / above them is not gud - SanŚ́́́́́́́́́́́́́́́́́-