TLDR Jump to the solution.
In the example below, we attempt to call HttpGetAsync() using the await keyword.
The HttpGetAsync() method is implemented correctly, and the call to HttpGetAsync() is also valid.
But the compiler complains:
Why?
Because, any function that uses await, must be declared with async.
When you use declare a function as async, the compiler sets up a state machine within the function, to keep track of the asynchronous calls. A state machine is necessary because although the calls within the function appear to execute synchronously (i.e. one after another), they actually execute as a sequence of method calls and callbacks. If you don't use async, the state machine won't be created, and any use of the await keyword will generate a compiler error.
In this case however, we don't want to make the Main() method async. In fact, we can't.
The entry point for a C# program can't be async. 1) It isn't supported by the .NET runtime, and 2) The program entry point must have a well defined signature. If you change it, the compiler won't be able to find it, and will report:
Error CS-5001 Program does not contain a static 'Main' method suitable for an entry point.
Example program demonstrating error CS4033:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace Zuga.net
{
class Program
{
static void Main(string[] args)
{
var url = "http://zuga.net/";
// Error CS4033 The 'await' operator can only be used within an async method.
// Consider marking this method with the 'async' modifier and changing its return type to 'Task'
string res = await HttpGetAsync(url);
Console.WriteLine(res);
}
public static async Task<string> HttpGetAsync(string uri)
{
string content = null;
var client = new HttpClient();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
}
return content;
}
}
}
The solution is to run the HttpGetAsync() call to completion, by executing a blocking call. The following 2 fixes are equivalent:
Option 1: Remove the await keyword, and wait for the task to complete by reading the task.Result property.
Option 2: Remove the await keyword, and wait for the task to complete by calling task.Wait().
Option 1: Block by reading task.Result
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace Zuga.net
{
class Program
{
static void Main(string[] args)
{
var url = "http://zuga.net/";
var task = HttpGetAsync(url);
string res = task.Result;
Console.WriteLine(res);
}
}
}
Option 2: Block by calling task.Wait()
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace Zuga.net
{
class Program
{
static void Main(string[] args)
{
var url = "http://zuga.net/";
var task = HttpGetAsync(url);
task.Wait();
string res = task.Result;
Console.WriteLine(res);
}
}
}
See also: