Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

c# - Explicitly use a Func<Task> for asynchronous lambda function when Action overload is available

Reading over this blog post on some of the gotchas of C#5's async/await. It mentions in Gotcha #4 something that is quite profound and that I hadn't thought of before.

Briefly, it covers the scenario where you have a method that has two overloads, one that takes an Action and one that takes a Func<Task> (for example Task.Run). This issue is rooted in the argument that async void methods should only be used for event handlers, with the post then going on to portray the following scenario - What does the compiler infer when a lambda function like the following can be compiled to both a Func<Task> and an Action:

Task.Run(async () => {
  await Task.Delay(1000);
});

Because Task.Run has signatures of both Task.Run(Func<Task>) and Task.Run(Action), what type is the async anonymous function compiled to? An async void or a Func<Task>? My gut feeling says it will compile down to an async void purely because its a non-generic type however the C# Compiler might be smart and give Func<Task> types preference.

Also, is there a way to explicitly declare which overload I wish to use? I know I could just create a new instance of Func<Task> and pass in the async lambda function there but it would still compile down to a async void and then pass that into the constructor of the Func<Task>. What is the ideal way to make sure its compiled as a Func<Task>?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Because Task.Run has signatures of both Task.Run(Func<Task>) and Task.Run(Action), what type is the async anonymous function compiled to? An async void or a Func<Task>? My gut feeling says it will compile down to an async void purely because its a non-generic type however the C# Compiler might be smart and give Func<Task> types preference.

The general rule, even without async, is that a delegate with a return type is a better match than a delegate without a return type. Another example of this is:

static void Foo(Action a) { }
static void Foo(Func<int> f) { }
static void Bar()
{
  Foo(() => { throw new Exception(); });
}

This is unambiguous and calls the second overload of Foo.

Also, is there a way to explicitly declare which overload I wish to use?

A nice way to make this clear is to specify the parameter name. The parameter names for the Action and Func<Task> overloads are different.

Task.Run(action: async () => {
  await Task.Delay(1000);
});
Task.Run(function: async () => {
  await Task.Delay(1000);
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...