Proposal:Lambda expression with separated closure #3467
Replies: 17 comments
-
I don't understand what is the proposed syntax supposed to achieve. Could you describe that, ideally including an example of how it would be used? |
Beta Was this translation helpful? Give feedback.
-
Why do you feel that these overloads are hard to use? What is this "missing lambda expression syntax support" and how does it change anything? Please provide some code examples of where the existing syntax is a problem and how/why whatever it is that you're proposing would change any of that. |
Beta Was this translation helpful? Give feedback.
-
When I use void Sample(int parameter)
{
Task.Factory.StartNew(()=>
{
DoSomeStuffWithParameter(parameter);
});
}
Every time we call this method,one closure (it captures parameter) and one delegate is allocated. When I want to do same thing with class Closure
{
internal int parameter;
}
void Sample(int parameter)
{
Task.Factory.StartNew((state)=>
{
var closure = (Closure)state;
DoSomeStuffWithParameter(closure.parameter);
},new Closure(){parameter = parameter});
} With this code,one closure has been created each time we call this method,but allocates delegate only once when we call this method at first. We must manually look up which parameter should be captured,and need to define closure explicitly.If looking up was not correct,this causes one more closure and delegate creation each time we call this method,and it would rather make slower performance. With proposed syntax,This should be done with following syntax. void Sample(int parameter)
{
(closure object state,Action<object> action) = (state)=>
{
DoSomeStuffWithParameter(parameter);
}
Task.Factory.StartNew(action,state);
Moreover, There are widely discussed syntax which provided only for avoid allocation from lambda expression. I want this functionality,but I don't obsess with this syntax. |
Beta Was this translation helpful? Give feedback.
-
I see, so what you want to see is for the allocated closure to be passed along as the IMO that API pattern is also a remnant from an earlier time before closures were common, I don't think you find that terribly often anymore. |
Beta Was this translation helpful? Give feedback.
-
Yes,we still need to create closure each time we call it,and I explicitly mentioned that.
I also mentioned about it advantages.
I think you are partialy right. Yeah,it seems like little bit ugry,but fast. |
Beta Was this translation helpful? Give feedback.
-
Seems like a lot of syntax for a feature that saves at most one allocation on fairly uncommon APIs. IMO in those rare situations you'd get more mileage out of a helper method that allowed you to pass a generic type as the state so that you can specify an anonymous class as the closure: using System;
using System.Threading.Tasks;
public class C {
public void M(int parameter) {
var task = StartNewTask(new { parameter },
state => DoSomethingWithParameter(state.parameter));
}
public void DoSomethingWithParameter(int parameter) { }
private Task StartNewTask<S>(S state, Action<S> action) {
return Task.Factory.StartNew(s => action((S)s), state);
}
private Task<T> StartNewTask<S, T>(S state, Func<S, T> action) {
return Task.Factory.StartNew(s => action((S)s), state);
}
} |
Beta Was this translation helpful? Give feedback.
-
Such a code generation is already done with
Really?
Looks like really good and it solves any problems I previously proposed,but it causes one more virtual call and one more function pointer call. |
Beta Was this translation helpful? Give feedback.
-
The return on investment for
Without that task-like feature and
9 APIs aren't a lot. It's probably a little more than a dozen in total. That's nothing. And these aren't APIs that developers commonly interact with directly. TPL and
Those helper methods aren't virtual, and they can be made static. They're simple enough to likely be inlined by JIT. |
Beta Was this translation helpful? Give feedback.
-
I mean that it is already mostly implemented, so we doesn't need to taking a cost so much about new code generation.
Perfectly separating features of C# and BCL library means "Any existing code is not a part of the C#".
We still need to use WaitHandle for waiting platform provided signals.
await Task.Delay() allocates Task every time we call it.
This API has been recently updated. Maybe because it is still common.
Your helper method itself is quite simple,and maybe inlined by JIT. |
Beta Was this translation helpful? Give feedback.
-
@RamType-0
It does, thanks to |
Beta Was this translation helpful? Give feedback.
-
Yeah,I know that,and I am using |
Beta Was this translation helpful? Give feedback.
-
@svick and also IMO,Adding |
Beta Was this translation helpful? Give feedback.
-
WIth this feature,we can widely reduces allocation by defining method which takes |
Beta Was this translation helpful? Give feedback.
-
I just find likely issues. |
Beta Was this translation helpful? Give feedback.
-
Adding one more: |
Beta Was this translation helpful? Give feedback.
-
The problem with this proposal is that you should specify the actual delegate type which is not identical to the one that is passed in. Today, we have: bool Any<T, TArg>(this ImmutableArray<T> array, Func<T, TArg, bool> predicate, TArg arg);
// manually pass intended captures
array.Any(static (item, t) => Use(item, t.a, t.b), (a, b)) Ideally we could rewrite that like this: bool Any<T>(this ImmutableArray<T> array, closure Func<T, bool> predicate);
// capturing a, b but actually not;
// those are passed implicitly to a compiler-generated parameter
array.Any(static item => Use(item, a, b)) But that I imagine we can take advantage of a function type syntax here, (derived from func pointers) // `closure` specifies that an additional parameter should be generated to pass captures
bool Any<T>(this ImmutableArray<T> array, closure delegate<T, bool> predicate);
array.Any(static item => Use(item, a, b)) But that still need the runtime support for delegate equivalence, since for a managed function type, we're going to generate managed delegates on the fly. Unless we just use function pointers, bool Any<T>(this ImmutableArray<T> array, closure delegate*<T, bool> predicate); I think that is actually workable. |
Beta Was this translation helpful? Give feedback.
-
Remember, function pointers require unsafe due to assembly unloading. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Backgrounds
There are many API in .NET which takes
Action action
orFunc<TResult> func
as argument.such as
TaskFactory.StartsNew
,ThreadPool.QueueUserWorkItem
.Most of these also have overloads which takes
Action<object>action,object state
orFunc<object,TResult> func,object state
.These overloads are useful to reduce delegate allocation.
But currently,these overloads are hard to use.Because of missing lambda expression syntax support.
Proposed Syntax
Beta Was this translation helpful? Give feedback.
All reactions