In the new version of the preview of Visual Studio "15" there appeared such a construction, which returns a link to the object:

static void Main(string[] args) { int[] numbers = { 0b1, 0b10, 0b100, 0b1000, 0b1_000, 0b10_0000 }; ref int r = ref Find(numbers, x => x > 4); WriteLine($"{r} == {numbers[4]}"); r = 0; WriteLine($"{r} == {numbers[4]}"); } static ref Find(int[] list, Func<int, bool> pred) { int i; for (i = 0; !pred(list[i]); i++) ; return ref list[i]; } 

What is the meaning of this innovation? Unless if we return object from a method, then we do not return it implicitly the link to it?

Local functions also appeared. What is their usability, is it impossible to do without them? In my opinion, this will reduce the quality of the code, since the function will be similar to the class with methods.

 class Program { int[] numbers = { 0b1, 0b10, 0b100, 0b1000, 0b1_0000, 0b10_0000 }; (int, int) Tally(IEnumerable<int> list) { } } 
  • 7
    Please make the code a text, not a picture. - Nofate
  • the second is the same circuit. But the first object returns if you do just return ie If you look at php, then $object1 = find() and object $object2 = find() are different objects. and if you just return the link is the same object. The approach is good for implementing the so-called register pattern, in which object1 will be equal to object2 . - Naumov
  • Both features with not too wide area of ​​applicability. 1. The ability to use the result of a function as lvalue For example, Max(ref a, ref b, ref c) = 10; - selection of the maximum of the arguments and assigning it a value. If we return a structure, it will not be necessary to create a copy of it. 2. One-time helper methods (it looks almost like lambda, only without allocating memory for the delegate). I wonder if you can use closures. - kmv
  • @kmv: Yes you can. - VladD
  • 3
    As far as I understand, this preview is not Visual Studio 2015 (it has already been released), but the next release, the version number of which is just 15. (VS 2015 has version number 14.) - VladD

2 answers 2

In the first part, this is interesting for value types.

There is not much difference with reference types, you work with the object by the original link or by its copy. But with value types, such as int , you get a copy of the value. For the ref returned, you can work with the original.

Thus, you reduce the amount of copying structures (which can be a problem in high-performance code).

In addition, you can write code like “find a point with the largest X and increase its Y ”, because it looks like functions like

 ref Point MaxBy(Point[] points, Func<Point, double> selector) { ref Point result = ref points[0]; for (int i = 1; i < points.Length; i++) if (selector(result) < selector(point[i])) result = ref point[i]; return result; } ref Point rightmost = MaxBy(points, p => pX); rightmost.Y += 1; 

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.

Additionally, local functions “see” local variables in the enclosing function, which means you don’t have to pass a bunch of auxiliary arguments to them.


Another use case for local functions is iterators and async functions. See it. If you have a code

 IEnumerable<int> GetOdd(IEnumerable<int> s) { if (s == null) throw new ArgumentNullException(); foreach (var v in s) if (v % 2 != 0) yield return v; } 

- then the check will be performed, and the exception is thrown only after the start of the transfer. That is the code

 IEnumerable<int> odds; try { odds = GetOdd(seq); } catch (ArgumentException) { return false; } foreach (var v in odds) Console.WriteLine(v); 

will not catch the exception. That is, the user will have to know whether the exception will be thrown during the call or during the transfer.

With local functions, you can write this:

 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(); } 

The exception will be thrown right away, when you call GetOdd , and not when enumerating. Obviously, the internal function only makes sense for GetOdd , so you should not set up a separate function for it at the external level, even if it is a private one.

  • four
    Well, finally, support for local functions did happen. A crutch with auxiliary external functions is still growing from Fortran, if memory serves, or maybe deeper. Then this principle was dragged to C, and from there, along with syntax, to other languages. - rdorn
  • @rdorn, but in gcc (C dialect) nested functions have been added for a long time. - avp
  • @avp well, then the dialect ... is it still not in the standard, or have I missed something? - rdorn
  • @rdorn, probably not (I do not study them, it seems like you). But, (imho) for linux writers this is not important, since there is always gcc there. - avp
  • one
    I know that the issue is a hundred years old at lunchtime, but yesterday I tested the speed of the local function and the private function similar to it. The results are very interesting: the local one (regardless of the complexity of the calculations) works faster than a similar private one by 11-12%. In the event that AggressiveInlining was set as private, the gain is reduced to 8-9%. But these values ​​are still significant. And according to this, the use of local functions instead of insignificant ones in relation to the general structure of private ones is not just welcomed, and I would even say - necessarily - Kir_Antipov

I do not pretend to be completely truthful, but from a logical point of view, everything is as follows:

If we remove the ref , then in the first snippet we will return not the list item, but its newly created copy. Those. if we modify it (copy), then the change will not be reflected in the original list. This is not exactly the behavior expected from the f-tion.

  • But, after all, this is true only in the case of using “structural” data types. [If I am not mistaken in terminology] That is, when there is a set of specific values ​​that are literally transmitted as a copy. But if we work with some class object, then there is no difference at all. For and so the reference is transmitted to the object. Regardless of whether there is a ref or not. (examples: list, array, objects of custom classes.) - Andrew
  • No, this is counterintuitive. There must be an explicit way to pass an object by reference. Without creating a copy. - gecube
  • Counterintuitiveness is already a question of the correct stylistics of writing code (!) And not the answer to the question asked :) Do not substitute for a concept. - Andrew