Introduce syntax in lambda to capture by value rather than by reference #2029
Replies: 19 comments 8 replies
-
What is this proposal attempting to achieve? Why would it be beneficial to change how the variable is captured and how would it differ from how the capture works today? |
Beta Was this translation helpful? Give feedback.
-
@YairHalberstadt I think you misunderstood my point (maybe I didn't express it clearly). When a variable is captured in a lambda, the variable is hoisted as a field in a closure type. Accessing the variable in the lambda actually accesses that field, and returns whatever value the field has at this point in time. It's "by reference" in the sense that it doesn't make a copy of the value when the lambda is created. |
Beta Was this translation helpful? Give feedback.
-
@thomaslevesque |
Beta Was this translation helpful? Give feedback.
-
Well, for one thing it would be more efficient (accessing a constant is faster than accessing a field). It also changes the semantic of the capture, in that the value in the lambda is an independent copy of the captured variable, which is often desirable (e.g. in a loop, no need to explicitly make a local copy of the loop variable). It also makes things simpler for code that works on expression trees. The specific use case that prompted this proposal was that in EF Core, the int lcid = CultureInfo.CurrentCulture.LCID;
var results = await _dbContext.Items
.Where(i => EF.Functions.FreeText(i.Text, searchTerm, lcid))
.ToListAsync(); because |
Beta Was this translation helpful? Give feedback.
-
So var temp = foo;
() => temp; |
Beta Was this translation helpful? Give feedback.
-
The variable still has to be hoisted into a field on a class instance. There isn't another mechanism through which the compiler can capture data into a delegate instance. You're really just changing when that field is assigned as it would then be a copy of the local (which may or may not be hoisted itself). The difference is more apparent with expressions since you're asking the compiler to embed the current value of |
Beta Was this translation helpful? Give feedback.
-
Semantically, yes, but technically the actual code of the lambda would be different |
Beta Was this translation helpful? Give feedback.
-
Ah, I see what you mean. Yes, the value would still be a field on the closure type, it wouldn't really be a constant. |
Beta Was this translation helpful? Give feedback.
-
This may have some benefits for multi-threaded code by reducing sharing. |
Beta Was this translation helpful? Give feedback.
-
Small nitpick. This behaviour changed in C# 5 (released 2012): A lambda within a foreach loop now captures the current value of the loop variable only; it's as though each iteration of the loop gets a new variable declaration. This is one of the very few times they've made a breaking change to the language. See Closing over the loop variable considered harmful, and Closing over the loop variable, part two by Eric Lippert for more details; they're the most authoritative sources I could find quickly. |
Beta Was this translation helpful? Give feedback.
-
@theunrepentantgeek yes, I'm aware of this. But there are other scenarios (e.g. |
Beta Was this translation helpful? Give feedback.
-
Another way of possibly the same same... Make a Lamba type such can be replaced out or leveraged similar to how to do for parallel, then just pass the spec defined options as the default and a user can change at a whim. Overall I think we already do this for local functions. I.e. a struct is used rather than a class. Would be trivial to add an attribute and options for further enhancement. Then finally the short hand which can be extrapolated from c++ and molded into its appropriate place in c#, [] .... |
Beta Was this translation helpful? Give feedback.
-
This proposal never attracted much interest, and is of limited value, so it probably won't happen. Closing to reduce the clutter. |
Beta Was this translation helpful? Give feedback.
-
Reactivating and moving to discussion. :) |
Beta Was this translation helpful? Give feedback.
-
This is something that's always felt a little broken to me. Capturing value types by reference has never been the most intuitive behavior. This is the whole reason lambdas in a loop were written incorrectly so often that they first added a warning for it and then changed the behavior of for loops. It was never a misunderstanding of for loop variables, it was unintuitive capture behavior. I get why the language designers didn't want to introduce a bunch of rules about when to capture by value and when to capture by reference, but IMHO there really ought to be a way to capture by value without resorting to the current hackish workaround. |
Beta Was this translation helpful? Give feedback.
-
This makes sense for LINQ expression trees, actually: many times a developer who uses a LINQ provider, be it EF or anything else, needs to pass in a Regarding delegates, I think Roslyn can be improved to detect and optimize read-only captured locals, together with related work with optimizing locals capturing in general, like something I proposed in #6287. But |
Beta Was this translation helpful? Give feedback.
-
I propose to directly introduce new symbols, thereby changing the behavior, so that there is no much intrusive modification. such as: "->" |
Beta Was this translation helpful? Give feedback.
-
I think a keyword, similar to "static" lambdas, that will indicate a capture by value would be nice |
Beta Was this translation helpful? Give feedback.
-
In C#, when a lambda captures a local, it always captures the variable, not the value. In C++, captures are specified explicitly, and it's possible to indicate whether a given variable should be captured by reference or by value.
It would be useful to have a keyword (e.g.
valueof
) to tell the compiler to capture the value rather than the variable:In an expression tree,
valueof(foo)
would be aConstantExpression
with the value offoo
at the time the expression is created.Beta Was this translation helpful? Give feedback.
All reactions