diff --git a/proposals/csharp-7.1/async-main.md b/proposals/csharp-7.1/async-main.md index c1cc22f..dff5618 100644 --- a/proposals/csharp-7.1/async-main.md +++ b/proposals/csharp-7.1/async-main.md @@ -17,7 +17,8 @@ It is very common when learning C#, when writing console-based utilities, and wh to call and `await` `async` methods from Main. Today we add a level of complexity here by forcing such `await`'ing to be done in a separate async method, which causes developers to need to write boilerplate like the following just to get started: -```C# + +```csharp public static void Main() { MainAsync().GetAwaiter().GetResult(); @@ -28,6 +29,7 @@ private static async Task MainAsync() ... // Main body here } ``` + We can remove the need for this boilerplate and make it easier to get started simply by allowing Main itself to be `async` such that `await`s can be used in it. @@ -35,7 +37,8 @@ We can remove the need for this boilerplate and make it easier to get started si [design]: #detailed-design The following signatures are currently allowed entrypoints: -```C# + +```csharp static void Main() static void Main(string[]) static int Main() @@ -43,12 +46,14 @@ static int Main(string[]) ``` We extend the list of allowed entrypoints to include: -``` + +```csharp static Task Main() static Task Main() static Task Main(string[]) static Task Main(string[]) ``` + To avoid compatibility risks, these new signatures will only be considered as valid entrypoints if no overloads of the previous set are present. The language / compiler will not require that the entrypoint be marked as `async`, though we expect the vast majority of uses will be marked as such. @@ -59,7 +64,8 @@ When one of these is identified as the entrypoint, the compiler will synthesize - ```static Task Main(string[])``` will result in the compiler emitting the equivalent of ```private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();``` Example usage: -```C# + +```csharp using System; using System.Net.Http; @@ -81,14 +87,17 @@ The main drawback is simply the additional complexity of supporting additional e Other variants considered: Allowing `async void`. We need to keep the semantics the same for code calling it directly, which would then make it difficult for a generated entrypoint to call it (no Task returned). We could solve this by generating two other methods, e.g. -```C# + +```csharp public static async void Main() { ... // await code } ``` + becomes -```C# + +```csharp public static async void Main() => await $MainTask(); private static void $EntrypointMain() => Main().GetAwaiter().GetResult(); @@ -98,6 +107,7 @@ private static async Task $MainTask() ... // await code } ``` + There are also concerns around encouraging usage of `async void`. Using "MainAsync" instead of "Main" as the name. While the async suffix is recommended for Task-returning methods, that's primarily about library functionality, which Main is not, and supporting additional entrypoint names beyond "Main" is not worth it. diff --git a/proposals/csharp-7.1/generics-pattern-match.md b/proposals/csharp-7.1/generics-pattern-match.md index 46d95dc..4617e30 100644 --- a/proposals/csharp-7.1/generics-pattern-match.md +++ b/proposals/csharp-7.1/generics-pattern-match.md @@ -8,7 +8,7 @@ ## Summary [summary]: #summary -The specification for the [existing C# as operator](https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#the-as-operator) permits there to be no conversion between the type of the operand and the specified type when either is an open type. However, in C# 7 the `Type identifier` pattern requires there be a conversion between the type of the input and the given type. +The specification for the [existing C# as operator](../../spec/expressions.md#the-as-operator) permits there to be no conversion between the type of the operand and the specified type when either is an open type. However, in C# 7 the `Type identifier` pattern requires there be a conversion between the type of the input and the given type. We propose to relax this and change `expression is Type identifier`, in addition to being permitted in the conditions when it is permitted in C# 7, to also be permitted when `expression as Type` would be allowed. Specifically, the new cases are cases where the type of the expression or the specified type is an open type. diff --git a/proposals/csharp-7.1/infer-tuple-names.md b/proposals/csharp-7.1/infer-tuple-names.md index 0cfc55a..9f29f1d 100644 --- a/proposals/csharp-7.1/infer-tuple-names.md +++ b/proposals/csharp-7.1/infer-tuple-names.md @@ -9,7 +9,7 @@ This parallels the behavior of anonymous types, which allow inferring member na This is particularly handy when using tuples in LINQ: -``` +```csharp // "c" and "result" have element names "f1" and "f2" var result = list.Select(c => (c.f1, c.f2)).Where(t => t.f2 == 1); ``` @@ -30,7 +30,8 @@ There are two parts to the change: Note that the rule for handling duplicates is different than that for anonymous types. For instance, `new { x.f1, x.f1 }` produces an error, but `(x.f1, x.f1)` would still be allowed (just without any inferred names). This avoids breaking existing tuple code. For consistency, the same would apply to tuples produced by deconstruction-assignments (in C#): -```C# + +```csharp // tuple has element names "f1" and "f2" var tuple = ((x.f1, x?.f2) = (1, 2)); ``` @@ -44,7 +45,7 @@ When using the C# 7.1 compiler (or later) with language version "7.0", the eleme The main drawback is that this introduces a compatibility break from C# 7.0: -```C# +```csharp Action y = () => M(); var t = (x: x, y); t.y(); // this might have previously picked up an extension method called “y”, but would now call the lambda.