-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Add Async support for Main() / Console Apps #1695
Comments
Sounds reasonable. I'd think that the compiler could automatically handle this by emitting a synchronous entry point method which would then invoke the asynchronous static class Program
{
[STAThread]
// can return either Task or Task<int>, and can optionally accept string[]
static async Task<int> Main(string[] args)
{
var client = new System.Net.Http.HttpClient();
Console.WriteLine(await client.GetStringAsync("http://www.microsoft.com"));
return 0;
}
} would be converted into: static class Program
{
[STAThread]
static async Task<int> Main(string[] args)
{
var client = new System.Net.Http.HttpClient();
Console.WriteLine(await client.GetStringAsync("http://www.microsoft.com"));
return 0;
}
[STAThread] // automatically copied by the compiler, default is MTAThread
// .entrypoint
static int <Main>_EntryPoint(string[] args) // synthetic method name
{
Task<int> task = Main(args);
return task.Result;
}
} This is of course greatly simplifying the problem. |
This would be great. We support this in DNX console applications by just waiting on the entrypoint that returns a task within the runtime code itself. Related to #1081 |
That could be a syntactic sugar for something like this: static int Main(string[] args)
{
Func<Task<int>> main =
async () =>
{
await Task.Delay(5000);
Console.WriteLine("Done...");
return 0;
};
return main().Result;
} |
BTW, we are going to do this for scripts: #4495 |
It should perhaps make sense to provide (at least optionally) a task scheduler which runs a kind of message loop, so that the execution flow can return to the initial (main) thread after |
👍 because I'm stick of typing this at the start of every project:
|
You can always write a new project template. |
I think we should do this. For static async Task Main(string[] args) { … } the compiler generates the equivalent of: static void $EntrypointMain(string[] args) { Main(args).GetAwaiter().GetResult(); }
static async Task Main(string[] args) { … } For static async Task<int> Main(string[] args) { … } the compiler generates the equivalent of: static int $EntrypointMain(string[] args) { return Main(args).GetAwaiter().GetResult(); }
static async Task<int> Main() { … } And optionally for static async void Main(string[] args) { … } the compiler would generate the equivalent of: static void $EntrypointMain(string[] args) { Main(args).GetAwaiter().GetResult(); }
static async Task Main(string[] args) { … } (One minor oddity with the void returning is that the return type will be different from what’s typed if reflection is used to access the method.) This keeps policy to an absolute minimum and doesn’t try to enforce any specific threading model, hence not doing anything special with regards to SynchronizationContext or TaskScheduler. For anything more complicated, a developer can write their own entrypoint that employs the logic they desire. |
@stephentoub This proposal looks good except for the re-writing of I think it would be better to just disallow (or ignore) this method signature and require an async Main to return a Task. You can have a diagnostic and quick fix to handle the case that the user writes the return type as void. |
@MgSam, yeah, I was conflicted about void-returning, which is why I wrote "And optionally...". On the one hand, the language supports async void methods, and part of the point of this feature would be to minimize the friction in starting to use async. Plus, since it's the entrypoint, and since async void methods do integrate with the SynchronizationContext to let it track when the operation has completed, it seems somewhat reasonable for the compiler to implement that tracking in a different way, by changing the return type. However, that's a bit disingenuous, as if it was actually using a SynchronizationContext to do the tracking, it'd be tracking all async void methods (and anything else calling SynchronizationContext.OperationStarted/Ended), not just this one, and we don't want that behavior. Further, I realized after posting this that changing the return type would be a bad thing, not just for reflection, but for anyone actually trying to invoke the method manually. So, in the end I also think it'd be better not to allow async void on entry points. |
👍 for simply banning |
This wasn't done for unit test methods, why should it be done here? And what about if there's already a Is there any other feature in C# where the compiler changes the source element like this? Should there be? Would that work by taking the /main: compiler switch? |
@paulomorgado, did you see my follow-up comments?
Not sure what you mean... several unit testing frameworks support async void tests.
My suggestion is that |
@stephentoub, I saw it now. I can't explain how and why my comment only got posted today, but I wrote it yesterday. |
Lets's make this happen! What's next? |
👍 for the simplicity of @stephentoub's solution. |
I know nothing. I see that the proposed solutions appear to begin and end at the process boundary. Does it make any sense to ask the runtime to consume a task? The idea is that our programs are not just simple synchronous executables in a sea of others, but rather, a potential set of async components that will # be strung together in some future shell scripting environment. In the age of async, I think we can demand that the runtime step-up. Again, I claim ignorance. |
👀 |
Question: Would there be a problem with updating compiler Not being able to follow normal naming convention here would make my code look sloppy to me. And since "Hello World" apps are often Console apps, |
This has been moved to dotnet/csharplang#97 |
Background
The console project template has shipped since Visual Studio 2002 and it continues to see high usage by developers who are either building command line interfaces, background application services (esp for Cloud), testing code, or just learning a new API.
The need for simple apps has been expanded as new project types like WebJobs, Cloud Service Worker Roles, and ASP.NET 5 Console applications have been added that provide a template for running background application services or cross-platform console apps respectively. The template below shows what's currently in Visual Studio 2013:
Problem
The problem arises in that a Main method cannot be marked as async. Given the rise of async/await in all Microsoft or 3rd party APIs, many customers have to write boilerplate code that's designed around the lack of support for async support in Main(), where they build another method and wait() for it.
In short:
Proposed Solution
The code I would expect would be that I could add the async keyword with a Task for the return type as shown below (friends don't let friends async void) and I can await() to my heart's content directly in a Main() method.
There still a number of things to iron out from an implementation perspective. In an email thread with Lucian Wischik and Mads Torgerson, they both raised some considerations to think about, including:
The text was updated successfully, but these errors were encountered: