From d320db0bfc74d7e04f88bdc4abbf67c999e6b819 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 10:28:55 +0200 Subject: [PATCH 01/19] c# 7 pattern matching --- csharp8/ExploreCsharpEight.Tests/Event.cs | 15 ++++++++ .../ExploreCsharpEight.Tests.csproj | 17 ++++++++++ .../PatternMatchingCSharp7Should.cs | 34 +++++++++++++++++++ .../ExploreCsharpEight/ExploreCsharpEight.sln | 31 +++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 csharp8/ExploreCsharpEight.Tests/Event.cs create mode 100644 csharp8/ExploreCsharpEight.Tests/ExploreCsharpEight.Tests.csproj create mode 100644 csharp8/ExploreCsharpEight.Tests/PatternMatchingCSharp7Should.cs create mode 100644 csharp8/ExploreCsharpEight/ExploreCsharpEight.sln diff --git a/csharp8/ExploreCsharpEight.Tests/Event.cs b/csharp8/ExploreCsharpEight.Tests/Event.cs new file mode 100644 index 0000000..7d7c98c --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/Event.cs @@ -0,0 +1,15 @@ +namespace ExploreCsharpEight.Tests +{ + public abstract class Event + { + + } + + public class OrderReceived : Event + { + } + + public class OrderAccepted : Event + { + } +} \ No newline at end of file diff --git a/csharp8/ExploreCsharpEight.Tests/ExploreCsharpEight.Tests.csproj b/csharp8/ExploreCsharpEight.Tests/ExploreCsharpEight.Tests.csproj new file mode 100644 index 0000000..bdde144 --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/ExploreCsharpEight.Tests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + 8.0 + false + + + + + + + + + + + diff --git a/csharp8/ExploreCsharpEight.Tests/PatternMatchingCSharp7Should.cs b/csharp8/ExploreCsharpEight.Tests/PatternMatchingCSharp7Should.cs new file mode 100644 index 0000000..3f0ee85 --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/PatternMatchingCSharp7Should.cs @@ -0,0 +1,34 @@ +using System; + +namespace ExploreCsharpEight.Tests +{ + + public class PatternMatchingCSharp7Should + { + public class OrderManager + { + public void Notify(Event evt) + { + switch (evt) + { + case OrderReceived orderReceived: + Handle(orderReceived); + break; + case OrderAccepted orderAccepted: + Handle(orderAccepted); + break; + default: + throw new ArgumentException(); + } + } + + private void Handle(OrderReceived orderReceived) + { + } + + private void Handle(OrderAccepted orderAccepted) + { + } + } + } +} \ No newline at end of file diff --git a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln new file mode 100644 index 0000000..75985b8 --- /dev/null +++ b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29920.165 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight", "ExploreCsharpEight.csproj", "{21BECE14-ED87-4D48-A399-27EA8F2B480A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExploreCsharpEight.Tests", "..\ExploreCsharpEight.Tests\ExploreCsharpEight.Tests.csproj", "{18F78165-32A4-42DC-90FE-70CBDEB09E97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {21BECE14-ED87-4D48-A399-27EA8F2B480A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21BECE14-ED87-4D48-A399-27EA8F2B480A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21BECE14-ED87-4D48-A399-27EA8F2B480A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21BECE14-ED87-4D48-A399-27EA8F2B480A}.Release|Any CPU.Build.0 = Release|Any CPU + {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B96EC500-418B-4550-AFE4-6302F967773B} + EndGlobalSection +EndGlobal From 8be487bd810dab1dfd96e6158ead3c389cc716d8 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 10:30:11 +0200 Subject: [PATCH 02/19] gitignore ncrunch --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2c338fe..c78bde7 100644 --- a/.gitignore +++ b/.gitignore @@ -358,3 +358,6 @@ NuGet.config # MacOS files .DS_Store **/.trydotnet-builderror + +# NCrunch +*ncrunch* From f1a8b602548a07adb01cc01b3b667ceaac382d5c Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 10:31:34 +0200 Subject: [PATCH 03/19] async stream --- .../AsyncStreamShould.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 csharp8/ExploreCsharpEight.Tests/AsyncStreamShould.cs diff --git a/csharp8/ExploreCsharpEight.Tests/AsyncStreamShould.cs b/csharp8/ExploreCsharpEight.Tests/AsyncStreamShould.cs new file mode 100644 index 0000000..fee272b --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/AsyncStreamShould.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NFluent; +using NUnit.Framework; + +namespace ExploreCsharpEight.Tests +{ + [TestFixture] + public class AsyncStreamShould + { + public static async System.Collections.Generic.IAsyncEnumerable GenerateSequence() + { + for (int i = 0; i < 20; i++) + { + await Task.Delay(1); + yield return i; + } + } + + public async Task> Consume(IAsyncEnumerable stream) + { + var list = new List(); + await foreach (var i in stream) + { + list.Add(i); + } + + return list; + } + + [Test] + public async Task Can_get_future_list() + { + var result = await Consume(GenerateSequence()); + Check.That(result).ContainsExactly(Enumerable.Range(0, 20)); + } + } +} \ No newline at end of file From e52ebed4748c03c31bf8ad90f48bcdd7d57b7059 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 12:20:24 +0200 Subject: [PATCH 04/19] Await a completed task does not re-run the task --- .../ExploreCsharpEight.Tests/TasksShould.cs | 29 +++++++++++++++++ .../ExploreCsharpEight/ExploreCsharpEight.sln | 8 ++++- csharp8/Tasks.Console/Program.cs | 31 +++++++++++++++++++ csharp8/Tasks.Console/Tasks.ConsoleApp.csproj | 8 +++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 csharp8/ExploreCsharpEight.Tests/TasksShould.cs create mode 100644 csharp8/Tasks.Console/Program.cs create mode 100644 csharp8/Tasks.Console/Tasks.ConsoleApp.csproj diff --git a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs new file mode 100644 index 0000000..71b2d5c --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using NFluent; +using NUnit.Framework; + +namespace ExploreCsharpEight.Tests +{ + [TestFixture] + public class TasksShould + { + [Test] + public async Task Await_multiple_times() + { + var result = 5; + Task pending = SomeMethodAsync(result); + Check.That(await pending).IsEqualTo(result); + Check.That(await pending).IsEqualTo(result); + Check.That(await pending).IsEqualTo(result); + Check.That(await pending).IsEqualTo(result); + Check.That(await pending).IsEqualTo(result); + Check.That(await pending).IsEqualTo(result); + } + + private async Task SomeMethodAsync(int result) + { + await Task.Delay(10); + return result; + } + } +} \ No newline at end of file diff --git a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln index 75985b8..b61bd7c 100644 --- a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln +++ b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln @@ -5,7 +5,9 @@ VisualStudioVersion = 16.0.29920.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight", "ExploreCsharpEight.csproj", "{21BECE14-ED87-4D48-A399-27EA8F2B480A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExploreCsharpEight.Tests", "..\ExploreCsharpEight.Tests\ExploreCsharpEight.Tests.csproj", "{18F78165-32A4-42DC-90FE-70CBDEB09E97}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight.Tests", "..\ExploreCsharpEight.Tests\ExploreCsharpEight.Tests.csproj", "{18F78165-32A4-42DC-90FE-70CBDEB09E97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tasks.ConsoleApp", "..\Tasks.Console\Tasks.ConsoleApp.csproj", "{ADBEB077-2B48-4466-84C3-067DE3A50AA4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Debug|Any CPU.Build.0 = Debug|Any CPU {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Release|Any CPU.ActiveCfg = Release|Any CPU {18F78165-32A4-42DC-90FE-70CBDEB09E97}.Release|Any CPU.Build.0 = Release|Any CPU + {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp8/Tasks.Console/Program.cs b/csharp8/Tasks.Console/Program.cs new file mode 100644 index 0000000..5e96b48 --- /dev/null +++ b/csharp8/Tasks.Console/Program.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; + +namespace Tasks.ConsoleApp +{ + class Program + { + static async Task Main(string[] args) + { + var result = 5; + Task pending = SomeMethodAsync(result); + + Console.WriteLine(await pending); + Console.WriteLine(await pending); + Console.WriteLine(await pending); + Console.WriteLine(await pending); + Console.WriteLine(await pending); + Console.WriteLine(await pending); + Console.WriteLine(await pending); + + Console.ReadKey(); + } + + + private static async Task SomeMethodAsync(int result) + { + await Task.Delay(TimeSpan.FromSeconds(5)); + return result; + } + } +} diff --git a/csharp8/Tasks.Console/Tasks.ConsoleApp.csproj b/csharp8/Tasks.Console/Tasks.ConsoleApp.csproj new file mode 100644 index 0000000..d453e9a --- /dev/null +++ b/csharp8/Tasks.Console/Tasks.ConsoleApp.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + From d9e8c9aefb9cd3e0f7869c82fe506a0f0c80e5a9 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 14:57:39 +0200 Subject: [PATCH 05/19] Task continuation --- Documentation/Revisit.md | 6 +++ .../ExploreCsharpEight.Tests/TasksShould.cs | 32 ++++++----- .../ExploreCsharpEight/ExploreCsharpEight.sln | 8 ++- csharp8/Tasks.Console/Program.cs | 26 ++++----- .../Controllers/WeatherForecastController.cs | 53 +++++++++++++++++++ csharp8/WebApp/Program.cs | 26 +++++++++ csharp8/WebApp/Properties/launchSettings.json | 30 +++++++++++ csharp8/WebApp/Startup.cs | 51 ++++++++++++++++++ csharp8/WebApp/WeatherForecast.cs | 23 ++++++++ csharp8/WebApp/WebApp.csproj | 10 ++++ csharp8/WebApp/appsettings.Development.json | 9 ++++ csharp8/WebApp/appsettings.json | 10 ++++ 12 files changed, 255 insertions(+), 29 deletions(-) create mode 100644 Documentation/Revisit.md create mode 100644 csharp8/WebApp/Controllers/WeatherForecastController.cs create mode 100644 csharp8/WebApp/Program.cs create mode 100644 csharp8/WebApp/Properties/launchSettings.json create mode 100644 csharp8/WebApp/Startup.cs create mode 100644 csharp8/WebApp/WeatherForecast.cs create mode 100644 csharp8/WebApp/WebApp.csproj create mode 100644 csharp8/WebApp/appsettings.Development.json create mode 100644 csharp8/WebApp/appsettings.json diff --git a/Documentation/Revisit.md b/Documentation/Revisit.md new file mode 100644 index 0000000..331d6a0 --- /dev/null +++ b/Documentation/Revisit.md @@ -0,0 +1,6 @@ +1. Where static value type member is allocated? +**On `Loader Heap`**. It can be shared by multiple threads. It is not boxed. It is not allocated on the stack. + [**SO question*](https://stackoverflow.com/questions/25741795/is-a-static-value-type-field-boxed-in-the-heap-in-c) + + 1. What is the advantage of `Task` over `Thread`? + diff --git a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs index 71b2d5c..1f406c9 100644 --- a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs +++ b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; using NFluent; using NUnit.Framework; @@ -8,22 +9,29 @@ namespace ExploreCsharpEight.Tests public class TasksShould { [Test] - public async Task Await_multiple_times() + public async Task Task_continuation_runs_on_a_thread_pool_thread() { - var result = 5; - Task pending = SomeMethodAsync(result); - Check.That(await pending).IsEqualTo(result); - Check.That(await pending).IsEqualTo(result); - Check.That(await pending).IsEqualTo(result); - Check.That(await pending).IsEqualTo(result); - Check.That(await pending).IsEqualTo(result); - Check.That(await pending).IsEqualTo(result); + var beforeThreadId = Thread.CurrentThread.ManagedThreadId; + await Task.Delay(1); + + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + Check.That(afterThreadId).IsNotEqualTo(beforeThreadId); // Well they can be different } - private async Task SomeMethodAsync(int result) + [Test] + public async Task Can_force_continuation_take_place_on_the_initial_synchronizationContext() { - await Task.Delay(10); - return result; + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); + + var beforeThreadId = Thread.CurrentThread.ManagedThreadId; + await Task.Run(async () => await Task.Delay(1)) + .ContinueWith((t, o) => + { + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + Check.That(afterThreadId).IsNotEqualTo(beforeThreadId); // Well they can be different + + + }, null, TaskScheduler.FromCurrentSynchronizationContext()); } } } \ No newline at end of file diff --git a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln index b61bd7c..5a5d8ef 100644 --- a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln +++ b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln @@ -7,7 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight", "Explo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight.Tests", "..\ExploreCsharpEight.Tests\ExploreCsharpEight.Tests.csproj", "{18F78165-32A4-42DC-90FE-70CBDEB09E97}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tasks.ConsoleApp", "..\Tasks.Console\Tasks.ConsoleApp.csproj", "{ADBEB077-2B48-4466-84C3-067DE3A50AA4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tasks.ConsoleApp", "..\Tasks.Console\Tasks.ConsoleApp.csproj", "{ADBEB077-2B48-4466-84C3-067DE3A50AA4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "..\WebApp\WebApp.csproj", "{704F4867-DC4F-4568-A31D-9E89C321DE8E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADBEB077-2B48-4466-84C3-067DE3A50AA4}.Release|Any CPU.Build.0 = Release|Any CPU + {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp8/Tasks.Console/Program.cs b/csharp8/Tasks.Console/Program.cs index 5e96b48..13a9b69 100644 --- a/csharp8/Tasks.Console/Program.cs +++ b/csharp8/Tasks.Console/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Tasks.ConsoleApp @@ -7,25 +8,18 @@ class Program { static async Task Main(string[] args) { - var result = 5; - Task pending = SomeMethodAsync(result); + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); - Console.WriteLine(await pending); - Console.WriteLine(await pending); - Console.WriteLine(await pending); - Console.WriteLine(await pending); - Console.WriteLine(await pending); - Console.WriteLine(await pending); - Console.WriteLine(await pending); - - Console.ReadKey(); - } + var beforeThreadId = Thread.CurrentThread.ManagedThreadId; + await Task.Run(async () => await Task.Delay(1)) + .ContinueWith((t, o) => + { + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + + Console.WriteLine($"Before = {beforeThreadId}; Continuation = {afterThreadId}"); + }, null, TaskScheduler.FromCurrentSynchronizationContext()); - private static async Task SomeMethodAsync(int result) - { - await Task.Delay(TimeSpan.FromSeconds(5)); - return result; } } } diff --git a/csharp8/WebApp/Controllers/WeatherForecastController.cs b/csharp8/WebApp/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..fb23245 --- /dev/null +++ b/csharp8/WebApp/Controllers/WeatherForecastController.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApp.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public async Task> Get() + { + var beforeThread = Thread.CurrentThread.ManagedThreadId; + var forecast = Forecast(new WeatherForecast{ThreadId = beforeThread}); + _ = forecast + .ContinueWith((t, o) => + { + Console.WriteLine(Thread.CurrentThread.ManagedThreadId); + }, null, TaskScheduler.Default); + + return await forecast; + } + + private static async Task> Forecast(WeatherForecast weatherForecast) + { + await Task.Delay(1); + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => + { + return weatherForecast.Modify(w => w.Date = DateTime.Now.AddDays(index)) + .Modify(w => w.TemperatureC = rng.Next(-20, 55)) + .Modify(w => w.Summary = Summaries[rng.Next(Summaries.Length)]); + }).ToArray(); + } + } +} diff --git a/csharp8/WebApp/Program.cs b/csharp8/WebApp/Program.cs new file mode 100644 index 0000000..bb2ae1a --- /dev/null +++ b/csharp8/WebApp/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace WebApp +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/csharp8/WebApp/Properties/launchSettings.json b/csharp8/WebApp/Properties/launchSettings.json new file mode 100644 index 0000000..436adb1 --- /dev/null +++ b/csharp8/WebApp/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:4617", + "sslPort": 44312 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApp": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/csharp8/WebApp/Startup.cs b/csharp8/WebApp/Startup.cs new file mode 100644 index 0000000..ad03128 --- /dev/null +++ b/csharp8/WebApp/Startup.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace WebApp +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/csharp8/WebApp/WeatherForecast.cs b/csharp8/WebApp/WeatherForecast.cs new file mode 100644 index 0000000..2f7b54e --- /dev/null +++ b/csharp8/WebApp/WeatherForecast.cs @@ -0,0 +1,23 @@ +using System; + +namespace WebApp +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + + public int ThreadId { get; set; } + + public WeatherForecast Modify(Action modify) + { + modify(this); + return this; + } + } +} diff --git a/csharp8/WebApp/WebApp.csproj b/csharp8/WebApp/WebApp.csproj new file mode 100644 index 0000000..c934769 --- /dev/null +++ b/csharp8/WebApp/WebApp.csproj @@ -0,0 +1,10 @@ + + + + netcoreapp3.1 + Windows + ..\ExploreCsharpEight + + + + diff --git a/csharp8/WebApp/appsettings.Development.json b/csharp8/WebApp/appsettings.Development.json new file mode 100644 index 0000000..dba68eb --- /dev/null +++ b/csharp8/WebApp/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/csharp8/WebApp/appsettings.json b/csharp8/WebApp/appsettings.json new file mode 100644 index 0000000..81ff877 --- /dev/null +++ b/csharp8/WebApp/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} From 81d6e583de6bd1861d26af095c999a530dba8222 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 15:03:30 +0200 Subject: [PATCH 06/19] Continue on initial thread --- .../Controllers/WeatherForecastController.cs | 7 +++---- csharp8/WebApp/Properties/launchSettings.json | 18 ++---------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/csharp8/WebApp/Controllers/WeatherForecastController.cs b/csharp8/WebApp/Controllers/WeatherForecastController.cs index fb23245..477bdae 100644 --- a/csharp8/WebApp/Controllers/WeatherForecastController.cs +++ b/csharp8/WebApp/Controllers/WeatherForecastController.cs @@ -30,10 +30,9 @@ public async Task> Get() var beforeThread = Thread.CurrentThread.ManagedThreadId; var forecast = Forecast(new WeatherForecast{ThreadId = beforeThread}); _ = forecast - .ContinueWith((t, o) => - { - Console.WriteLine(Thread.CurrentThread.ManagedThreadId); - }, null, TaskScheduler.Default); + .ConfigureAwait(true); + + Console.WriteLine(Thread.CurrentThread.ManagedThreadId); return await forecast; } diff --git a/csharp8/WebApp/Properties/launchSettings.json b/csharp8/WebApp/Properties/launchSettings.json index 436adb1..28a32f5 100644 --- a/csharp8/WebApp/Properties/launchSettings.json +++ b/csharp8/WebApp/Properties/launchSettings.json @@ -1,22 +1,8 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:4617", - "sslPort": 44312 - } - }, + "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "weatherforecast", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, + "WebApp": { "commandName": "Project", "launchBrowser": true, From 3ed46a7686b95ac80aa4e1150ca402278dd70ded Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 16:20:24 +0200 Subject: [PATCH 07/19] continueOnCapturedContext, ConfigureAwait myth --- .../ExploreCsharpEight.Tests/TasksShould.cs | 22 ++++++++++++ .../Controllers/WeatherForecastController.cs | 35 +++++++++++++------ csharp8/WebApp/WeatherForecast.cs | 4 +-- csharp8/WebApp/appsettings.Development.json | 2 +- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs index 1f406c9..62b6d3b 100644 --- a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs +++ b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs @@ -33,5 +33,27 @@ await Task.Run(async () => await Task.Delay(1)) }, null, TaskScheduler.FromCurrentSynchronizationContext()); } + + [Test] + public async Task Can_force_continuation_on_inital_thread() + { + object scheduler = SynchronizationContext.Current; + if (scheduler is null/* && TaskScheduler.Current != TaskScheduler.Default*/) + { + scheduler = TaskScheduler.Current; + } + + Check.That(scheduler).IsNotNull(); + + var beforeThreadId = Thread.CurrentThread.ManagedThreadId; + await Task.Run(async () => await Task.Delay(1)) + .ContinueWith((t, o) => + { + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + Check.That(afterThreadId).IsEqualTo(beforeThreadId); // Well they can be different + + + }, null, TaskScheduler.Current); + } } } \ No newline at end of file diff --git a/csharp8/WebApp/Controllers/WeatherForecastController.cs b/csharp8/WebApp/Controllers/WeatherForecastController.cs index 477bdae..f1366e7 100644 --- a/csharp8/WebApp/Controllers/WeatherForecastController.cs +++ b/csharp8/WebApp/Controllers/WeatherForecastController.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -27,25 +29,38 @@ public WeatherForecastController(ILogger logger) [HttpGet] public async Task> Get() { + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); + _logger.LogDebug($"Current sync context = {SynchronizationContext.Current}, isNull = {SynchronizationContext.Current == null}"); + + var beforeThread = Thread.CurrentThread.ManagedThreadId; - var forecast = Forecast(new WeatherForecast{ThreadId = beforeThread}); - _ = forecast - .ConfigureAwait(true); - Console.WriteLine(Thread.CurrentThread.ManagedThreadId); + var continueOnCapturedContext = false; + + var forecastedDays = await Forecast(new WeatherForecast { CapturedInitialThread = beforeThread }) + .ConfigureAwait(continueOnCapturedContext); + + _logger.LogDebug($"{Thread.CurrentThread.ManagedThreadId} - After completed. "); - return await forecast; + + return forecastedDays; } - private static async Task> Forecast(WeatherForecast weatherForecast) + private async Task> Forecast(WeatherForecast weatherForecast) { - await Task.Delay(1); + var uri = new Uri("http://www.google.com"); + var client = new HttpClient {BaseAddress = uri}; + + _logger.LogDebug($"{Thread.CurrentThread.ManagedThreadId} - In task, Before await. "); + _logger.LogDebug($"Get HTTP Response {(await client.GetAsync(uri)).GetHashCode()}"); + + _logger.LogDebug($"{Thread.CurrentThread.ManagedThreadId} - In task, After await. "); var rng = new Random(); return Enumerable.Range(1, 5).Select(index => { - return weatherForecast.Modify(w => w.Date = DateTime.Now.AddDays(index)) - .Modify(w => w.TemperatureC = rng.Next(-20, 55)) - .Modify(w => w.Summary = Summaries[rng.Next(Summaries.Length)]); + return weatherForecast.With(w => w.Date = DateTime.Now.AddDays(index)) + .With(w => w.TemperatureC = rng.Next(-20, 55)) + .With(w => w.Summary = Summaries[rng.Next(Summaries.Length)]); }).ToArray(); } } diff --git a/csharp8/WebApp/WeatherForecast.cs b/csharp8/WebApp/WeatherForecast.cs index 2f7b54e..495087c 100644 --- a/csharp8/WebApp/WeatherForecast.cs +++ b/csharp8/WebApp/WeatherForecast.cs @@ -12,9 +12,9 @@ public class WeatherForecast public string Summary { get; set; } - public int ThreadId { get; set; } + public int CapturedInitialThread { get; set; } - public WeatherForecast Modify(Action modify) + public WeatherForecast With(Action modify) { modify(this); return this; diff --git a/csharp8/WebApp/appsettings.Development.json b/csharp8/WebApp/appsettings.Development.json index dba68eb..e2026c4 100644 --- a/csharp8/WebApp/appsettings.Development.json +++ b/csharp8/WebApp/appsettings.Development.json @@ -1,7 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Debug", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } From b8b5d79dca3db15ac37c4aadd5e98b0413196af1 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 16:43:32 +0200 Subject: [PATCH 08/19] Cross thread failure: Form1.cs L27 --- .../ExploreCsharpEight/ExploreCsharpEight.sln | 6 + csharp8/WindowsFormsApp/App.config | 6 + csharp8/WindowsFormsApp/Form1.Designer.cs | 72 +++++++++++ csharp8/WindowsFormsApp/Form1.cs | 41 ++++++ csharp8/WindowsFormsApp/Form1.resx | 120 ++++++++++++++++++ csharp8/WindowsFormsApp/Program.cs | 22 ++++ .../Properties/AssemblyInfo.cs | 36 ++++++ .../Properties/Resources.Designer.cs | 71 +++++++++++ .../WindowsFormsApp/Properties/Resources.resx | 117 +++++++++++++++++ .../Properties/Settings.Designer.cs | 30 +++++ .../Properties/Settings.settings | 7 + .../WindowsFormsApp/WindowsFormsApp.csproj | 83 ++++++++++++ 12 files changed, 611 insertions(+) create mode 100644 csharp8/WindowsFormsApp/App.config create mode 100644 csharp8/WindowsFormsApp/Form1.Designer.cs create mode 100644 csharp8/WindowsFormsApp/Form1.cs create mode 100644 csharp8/WindowsFormsApp/Form1.resx create mode 100644 csharp8/WindowsFormsApp/Program.cs create mode 100644 csharp8/WindowsFormsApp/Properties/AssemblyInfo.cs create mode 100644 csharp8/WindowsFormsApp/Properties/Resources.Designer.cs create mode 100644 csharp8/WindowsFormsApp/Properties/Resources.resx create mode 100644 csharp8/WindowsFormsApp/Properties/Settings.Designer.cs create mode 100644 csharp8/WindowsFormsApp/Properties/Settings.settings create mode 100644 csharp8/WindowsFormsApp/WindowsFormsApp.csproj diff --git a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln index 5a5d8ef..1d10746 100644 --- a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln +++ b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tasks.ConsoleApp", "..\Task EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "..\WebApp\WebApp.csproj", "{704F4867-DC4F-4568-A31D-9E89C321DE8E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsFormsApp", "..\WindowsFormsApp\WindowsFormsApp.csproj", "{334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Debug|Any CPU.Build.0 = Debug|Any CPU {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Release|Any CPU.ActiveCfg = Release|Any CPU {704F4867-DC4F-4568-A31D-9E89C321DE8E}.Release|Any CPU.Build.0 = Release|Any CPU + {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Debug|Any CPU.Build.0 = Debug|Any CPU + {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Release|Any CPU.ActiveCfg = Release|Any CPU + {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp8/WindowsFormsApp/App.config b/csharp8/WindowsFormsApp/App.config new file mode 100644 index 0000000..5754728 --- /dev/null +++ b/csharp8/WindowsFormsApp/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/csharp8/WindowsFormsApp/Form1.Designer.cs b/csharp8/WindowsFormsApp/Form1.Designer.cs new file mode 100644 index 0000000..33a1652 --- /dev/null +++ b/csharp8/WindowsFormsApp/Form1.Designer.cs @@ -0,0 +1,72 @@ +namespace WindowsFormsApp +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(108, 62); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(380, 75); + this.button1.TabIndex = 0; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(108, 198); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(380, 20); + this.textBox1.TabIndex = 1; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(601, 316); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.button1); + this.Name = "Form1"; + this.Text = "Form1"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox1; + } +} + diff --git a/csharp8/WindowsFormsApp/Form1.cs b/csharp8/WindowsFormsApp/Form1.cs new file mode 100644 index 0000000..4ac1d51 --- /dev/null +++ b/csharp8/WindowsFormsApp/Form1.cs @@ -0,0 +1,41 @@ +using System; +using System.Diagnostics; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WindowsFormsApp +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + } + + private string ThreadId() + { + return Thread.CurrentThread.ManagedThreadId.ToString( );} + + private async void button1_Click(object sender, EventArgs e) + { + Debug.WriteLine($"Main: {ThreadId()}"); + var hash = await DownloadGoogle().ConfigureAwait(false); + + Debug.WriteLine($"Write hash: {ThreadId()}"); + this.textBox1.Text = hash; + } + + private async Task DownloadGoogle() + { + var uri = new Uri("http://www.google.com"); + var client = new HttpClient { BaseAddress = uri }; + + var downloadGoogle = (await client.GetAsync(uri)).GetHashCode().ToString(); + + Debug.WriteLine($"After download: {ThreadId()}"); + return downloadGoogle; + } + } +} diff --git a/csharp8/WindowsFormsApp/Form1.resx b/csharp8/WindowsFormsApp/Form1.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/csharp8/WindowsFormsApp/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/csharp8/WindowsFormsApp/Program.cs b/csharp8/WindowsFormsApp/Program.cs new file mode 100644 index 0000000..b7d0238 --- /dev/null +++ b/csharp8/WindowsFormsApp/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WindowsFormsApp +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/csharp8/WindowsFormsApp/Properties/AssemblyInfo.cs b/csharp8/WindowsFormsApp/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8c6601c --- /dev/null +++ b/csharp8/WindowsFormsApp/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WindowsFormsApp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WindowsFormsApp")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("334dbdc4-a8a4-4e83-a68b-a8bcdad8c191")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/csharp8/WindowsFormsApp/Properties/Resources.Designer.cs b/csharp8/WindowsFormsApp/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d6f629c --- /dev/null +++ b/csharp8/WindowsFormsApp/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowsFormsApp.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowsFormsApp.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/csharp8/WindowsFormsApp/Properties/Resources.resx b/csharp8/WindowsFormsApp/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/csharp8/WindowsFormsApp/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/csharp8/WindowsFormsApp/Properties/Settings.Designer.cs b/csharp8/WindowsFormsApp/Properties/Settings.Designer.cs new file mode 100644 index 0000000..fc4be97 --- /dev/null +++ b/csharp8/WindowsFormsApp/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowsFormsApp.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/csharp8/WindowsFormsApp/Properties/Settings.settings b/csharp8/WindowsFormsApp/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/csharp8/WindowsFormsApp/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/csharp8/WindowsFormsApp/WindowsFormsApp.csproj b/csharp8/WindowsFormsApp/WindowsFormsApp.csproj new file mode 100644 index 0000000..b95acd5 --- /dev/null +++ b/csharp8/WindowsFormsApp/WindowsFormsApp.csproj @@ -0,0 +1,83 @@ + + + + + Debug + AnyCPU + {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191} + WinExe + WindowsFormsApp + WindowsFormsApp + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + + Form1.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file From aca3d46cfab7c8b396bfcb846b0c84211195dd05 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 16:45:48 +0200 Subject: [PATCH 09/19] fix cross thread issue --- csharp8/WindowsFormsApp/Form1.Designer.cs | 16 ++++++++++++++-- csharp8/WindowsFormsApp/Form1.cs | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/csharp8/WindowsFormsApp/Form1.Designer.cs b/csharp8/WindowsFormsApp/Form1.Designer.cs index 33a1652..28d4184 100644 --- a/csharp8/WindowsFormsApp/Form1.Designer.cs +++ b/csharp8/WindowsFormsApp/Form1.Designer.cs @@ -30,13 +30,14 @@ private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); this.SuspendLayout(); // // button1 // - this.button1.Location = new System.Drawing.Point(108, 62); + this.button1.Location = new System.Drawing.Point(108, 24); this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(380, 75); + this.button1.Size = new System.Drawing.Size(380, 52); this.button1.TabIndex = 0; this.button1.Text = "button1"; this.button1.UseVisualStyleBackColor = true; @@ -49,11 +50,21 @@ private void InitializeComponent() this.textBox1.Size = new System.Drawing.Size(380, 20); this.textBox1.TabIndex = 1; // + // button2 + // + this.button2.Location = new System.Drawing.Point(108, 102); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(380, 55); + this.button2.TabIndex = 2; + this.button2.Text = "button2"; + this.button2.UseVisualStyleBackColor = true; + // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(601, 316); + this.Controls.Add(this.button2); this.Controls.Add(this.textBox1); this.Controls.Add(this.button1); this.Name = "Form1"; @@ -67,6 +78,7 @@ private void InitializeComponent() private System.Windows.Forms.Button button1; private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button2; } } diff --git a/csharp8/WindowsFormsApp/Form1.cs b/csharp8/WindowsFormsApp/Form1.cs index 4ac1d51..21b2a45 100644 --- a/csharp8/WindowsFormsApp/Form1.cs +++ b/csharp8/WindowsFormsApp/Form1.cs @@ -21,7 +21,7 @@ private string ThreadId() private async void button1_Click(object sender, EventArgs e) { Debug.WriteLine($"Main: {ThreadId()}"); - var hash = await DownloadGoogle().ConfigureAwait(false); + var hash = await DownloadGoogle().ConfigureAwait(true); Debug.WriteLine($"Write hash: {ThreadId()}"); this.textBox1.Text = hash; From a6849c6043ca815744703fcb872a404dcf98bddb Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 16:57:01 +0200 Subject: [PATCH 10/19] Another way to change UI after async operation --- csharp8/WindowsFormsApp/Form1.Designer.cs | 1 + csharp8/WindowsFormsApp/Form1.cs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/csharp8/WindowsFormsApp/Form1.Designer.cs b/csharp8/WindowsFormsApp/Form1.Designer.cs index 28d4184..4428404 100644 --- a/csharp8/WindowsFormsApp/Form1.Designer.cs +++ b/csharp8/WindowsFormsApp/Form1.Designer.cs @@ -58,6 +58,7 @@ private void InitializeComponent() this.button2.TabIndex = 2; this.button2.Text = "button2"; this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); // // Form1 // diff --git a/csharp8/WindowsFormsApp/Form1.cs b/csharp8/WindowsFormsApp/Form1.cs index 21b2a45..b04a753 100644 --- a/csharp8/WindowsFormsApp/Form1.cs +++ b/csharp8/WindowsFormsApp/Form1.cs @@ -21,7 +21,9 @@ private string ThreadId() private async void button1_Click(object sender, EventArgs e) { Debug.WriteLine($"Main: {ThreadId()}"); - var hash = await DownloadGoogle().ConfigureAwait(true); + var hash = await DownloadGoogle() + .ConfigureAwait(true); // This is default behaviour + // Equivalent to: var hash = await DownloadGoogle() Debug.WriteLine($"Write hash: {ThreadId()}"); this.textBox1.Text = hash; @@ -32,10 +34,23 @@ private async Task DownloadGoogle() var uri = new Uri("http://www.google.com"); var client = new HttpClient { BaseAddress = uri }; + Debug.WriteLine($"Before download: {ThreadId()}"); + var downloadGoogle = (await client.GetAsync(uri)).GetHashCode().ToString(); Debug.WriteLine($"After download: {ThreadId()}"); return downloadGoogle; } + + private async void button2_Click(object sender, EventArgs e) + { + Debug.WriteLine($"Main: {ThreadId()}"); + await DownloadGoogle().ContinueWith(async (Task task, object state) => + { + var hash = await task; + Debug.WriteLine($"Write hash: {ThreadId()}"); + this.textBox1.Text = hash; + }, null, TaskScheduler.FromCurrentSynchronizationContext()); + } } } From d38cdd375d3d0c7e10c142544269579de410c80d Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Tue, 31 Mar 2020 17:40:18 +0200 Subject: [PATCH 11/19] In an unit test, I failed to force executing the continuation on the initial thread --- .../ExploreCsharpEight.Tests/TasksShould.cs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs index 62b6d3b..3f22fec 100644 --- a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs +++ b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using NFluent; @@ -13,7 +14,7 @@ public async Task Task_continuation_runs_on_a_thread_pool_thread() { var beforeThreadId = Thread.CurrentThread.ManagedThreadId; await Task.Delay(1); - + var afterThreadId = Thread.CurrentThread.ManagedThreadId; Check.That(afterThreadId).IsNotEqualTo(beforeThreadId); // Well they can be different } @@ -35,25 +36,14 @@ await Task.Run(async () => await Task.Delay(1)) } [Test] - public async Task Can_force_continuation_on_inital_thread() + public async Task Can_not_force_continuation_on_initial_thread() { - object scheduler = SynchronizationContext.Current; - if (scheduler is null/* && TaskScheduler.Current != TaskScheduler.Default*/) - { - scheduler = TaskScheduler.Current; - } - - Check.That(scheduler).IsNotNull(); - var beforeThreadId = Thread.CurrentThread.ManagedThreadId; - await Task.Run(async () => await Task.Delay(1)) - .ContinueWith((t, o) => - { - var afterThreadId = Thread.CurrentThread.ManagedThreadId; - Check.That(afterThreadId).IsEqualTo(beforeThreadId); // Well they can be different + await Task.Delay(10).ConfigureAwait(continueOnCapturedContext: true); - }, null, TaskScheduler.Current); + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + Check.That(afterThreadId).IsNotEqualTo(beforeThreadId); } } } \ No newline at end of file From 4eeebc93216ff992d714603c3d21ab7069aa061b Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:03:11 +0200 Subject: [PATCH 12/19] SynchornizationContext experiment --- csharp8/ExploreCsharpEight.Tests/TasksShould.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs index 3f22fec..79968b3 100644 --- a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs +++ b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs @@ -22,8 +22,14 @@ public async Task Task_continuation_runs_on_a_thread_pool_thread() [Test] public async Task Can_force_continuation_take_place_on_the_initial_synchronizationContext() { + Check.That(SynchronizationContext.Current).IsNull(); + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); + Check.That(SynchronizationContext.Current).IsNotNull(); + + var captureSyncContext = SynchronizationContext.Current; + var beforeThreadId = Thread.CurrentThread.ManagedThreadId; await Task.Run(async () => await Task.Delay(1)) .ContinueWith((t, o) => @@ -33,6 +39,10 @@ await Task.Run(async () => await Task.Delay(1)) }, null, TaskScheduler.FromCurrentSynchronizationContext()); + + Check.That(Thread.CurrentThread.ManagedThreadId).IsNotEqualTo(beforeThreadId); + Check.That(SynchronizationContext.Current).IsNotEqualTo(captureSyncContext); + Check.That(SynchronizationContext.Current).IsNull(); } [Test] From d54b463ae81fa2312db91811f2fb9fec0f629f96 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:04:56 +0200 Subject: [PATCH 13/19] Stupide coding game kata --- .../CodingGame.Enemy/CodingGame.Enemy.csproj | 8 ++ csharp8/CodingGame.Enemy/Player.cs | 92 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 csharp8/CodingGame.Enemy/CodingGame.Enemy.csproj create mode 100644 csharp8/CodingGame.Enemy/Player.cs diff --git a/csharp8/CodingGame.Enemy/CodingGame.Enemy.csproj b/csharp8/CodingGame.Enemy/CodingGame.Enemy.csproj new file mode 100644 index 0000000..d453e9a --- /dev/null +++ b/csharp8/CodingGame.Enemy/CodingGame.Enemy.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/csharp8/CodingGame.Enemy/Player.cs b/csharp8/CodingGame.Enemy/Player.cs new file mode 100644 index 0000000..60f2306 --- /dev/null +++ b/csharp8/CodingGame.Enemy/Player.cs @@ -0,0 +1,92 @@ +using System; +using System.Linq; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +/** + * The code below will read all the game information for you. + * On each game turn, information will be available on the standard input, you will be sent: + * -> the total number of visible enemies + * -> for each enemy, its name and distance from you + * The system will wait for you to write an enemy name on the standard output. + * Once you have designated a target: + * -> the cannon will shoot + * -> the enemies will move + * -> new info will be available for you to read on the standard input. + * + * + * + * Votre programme doit détruire les vaisseaux ennemis en tirant sur l'ennemi le plus proche à chaque tour. + Règles + Les vaisseaux ennemis approchent en ligne droite vers votre canon. + + À chaque début d'un tour de jeu (dans la boucle game loop), vous obtenez les informations des deux ennemis + les plus proches : + variable enemy1 : le nom de l'ennemi 1. + variable dist1 : la distance à laquelle se trouve l'ennemi 1. + variable enemy2 : le nom de l'ennemi 2. + variable dist2 : la distance à laquelle se trouve l'ennemi 2. + Avant la fin du tour (fin de la boucle), vous devez indiquer en sortie le nom de l'ennemi le plus proche. + Pour afficher le nom de l'ennemi le plus proche, vous devez utiliser la variable enemy1 ou enemy2. + **/ +class Player +{ + + static void Main(string[] args) + { + // game loop + while (true) + { + + int minDistance = 1000; + string closestEnemy = null; + + + if (!int.TryParse(Console.ReadLine(), out int count)) + { + Console.WriteLine("Invalid arguments."); + continue; + } + + for (int i = 0; i < count; i++) + { + try + { + (string enemy, int dist) = ParseEnemyInfo(Console.ReadLine()); + if (dist < minDistance) + { + minDistance = dist; + closestEnemy = enemy; + } + } + catch (ArgumentException) + { + Console.WriteLine("Invalid arguments."); + continue; + } + + } + + // Write an action using Console.WriteLine() + // To debug: Console.Error.WriteLine("Debug messages..."); + Console.Out.WriteLine(closestEnemy); + //Console.WriteLine("HotDroid"); // The name of the most threatening enemy (HotDroid is just one example) + } + } + + private static (string enemy, int dist) ParseEnemyInfo(string rawEnemy) + { + string[] components = Regex.Split(rawEnemy, @"\s+"); + if (components.Length != 2 + || !int.TryParse(components[1].Substring(0, components[1].Length - 1), out int distance)) + { + throw new ArgumentException("Invalid enemy info"); + } + + var enemy = components[0]; + return (enemy, distance); + } +} \ No newline at end of file From 6ee21ffc7a35e8aa9722c2bee8f12460a71e025b Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:06:04 +0200 Subject: [PATCH 14/19] fixup! SynchornizationContext experiment --- Documentation/Revisit.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/Revisit.md b/Documentation/Revisit.md index 331d6a0..c28746c 100644 --- a/Documentation/Revisit.md +++ b/Documentation/Revisit.md @@ -2,5 +2,15 @@ **On `Loader Heap`**. It can be shared by multiple threads. It is not boxed. It is not allocated on the stack. [**SO question*](https://stackoverflow.com/questions/25741795/is-a-static-value-type-field-boxed-in-the-heap-in-c) - 1. What is the advantage of `Task` over `Thread`? +1. What is the advantage of `Task` over `Thread`? +1. `SynchronizationContext` + + https://docs.microsoft.com/en-us/archive/msdn-magazine/2011/february/msdn-magazine-parallel-computing-it-s-all-about-the-synchronizationcontext + + > 1) One aspect of SynchronizationContext is that it provides a way to queue a unit of work to a context. Note that this unit of work is queued to a context rather than a specific thread. + + + > 2) Another aspect of SynchronizationContext is that every thread has a “current” context. A thread’s context isn’t necessarily unique; its context instance may be shared with other threads. It’s possible for a thread to change its current context, but this is quite rare. + + > 3) A third aspect of SynchronizationContext is that it **keeps a count of** outstanding asynchronous operations. \ No newline at end of file From e5b2cbe068d00f63c2ea7660a444efb979d9c122 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:06:27 +0200 Subject: [PATCH 15/19] Regular expression --- .../RegularExpressionShould.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 csharp8/ExploreCsharpEight.Tests/RegularExpressionShould.cs diff --git a/csharp8/ExploreCsharpEight.Tests/RegularExpressionShould.cs b/csharp8/ExploreCsharpEight.Tests/RegularExpressionShould.cs new file mode 100644 index 0000000..06a083e --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/RegularExpressionShould.cs @@ -0,0 +1,63 @@ +using System; +using System.Text.RegularExpressions; +using NFluent; +using NUnit.Framework; +using NUnit.Framework.Internal; + +namespace ExploreCsharpEight.Tests +{ + [TestFixture] + public class RegularExpressionShould + { + [TestCase("toto 15", true)] + [TestCase("toto6966 15", true)] + [TestCase("toto6966 15d", false)] + [TestCase("toto6966 15", true)] + [TestCase("42 15", true)] + public void Match_string_space_integer(string input, bool expectedMatch) + { + var regex = new Regex(@"^\w+\s+\d+$"); + bool match = regex.IsMatch(input); + + Check.That(match).IsEqualTo(expectedMatch); + } + + [Test] + public void Match_one_value() + { + // Step 1: create new Regex. + Regex regex = new Regex(@"\d+"); + + // Step 2: call Match on Regex instance. + Match match = regex.Match("Dot 55 Perls"); + + // Step 3: test for Success. + if (match.Success) + { + Console.WriteLine("MATCH VALUE: " + match.Value); + } + } + + [Test] + public void Take_out_matching_values() + { + // Part 1: the input string. + string input = "/content/alternate-1.aspx"; + + // Part 2: call Regex.Match. + Match match = Regex.Match(input, @"content/([A-Za-z0-9\-]+)\.aspx$", + RegexOptions.IgnoreCase); + + // Part 3: check the Match for Success. + if(!match.Success) + Assert.Fail("Not match"); + + + // Part 4: get the Group value and display it. + Check.That(match.Groups).HasSize(2); + + Check.That(match.Groups[0].Value).IsEqualTo("content/alternate-1.aspx"); + Check.That(match.Groups[1].Value).IsEqualTo("alternate-1"); + } + } +} \ No newline at end of file From 84fe45a394318aa6af7c227b29f77acbbe3cabb8 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:08:38 +0200 Subject: [PATCH 16/19] Task yield (what it is for? ) usecase 1: force exec in a different thread --- .../TaskYieldShould.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 csharp8/ExploreCsharpEight.Tests/TaskYieldShould.cs diff --git a/csharp8/ExploreCsharpEight.Tests/TaskYieldShould.cs b/csharp8/ExploreCsharpEight.Tests/TaskYieldShould.cs new file mode 100644 index 0000000..6b024f4 --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/TaskYieldShould.cs @@ -0,0 +1,41 @@ +using System.Threading; +using System.Threading.Tasks; +using NFluent; +using NUnit.Framework; + +namespace ExploreCsharpEight.Tests +{ + [TestFixture] + public class TaskYieldShould + { + [Test] + public async Task Without_task_yield_actually_get_a_real_blocking_method_call() + { + var capture = Thread.CurrentThread.ManagedThreadId; + var threadOfAsyncOp = await DoFakeAsync(); + Check.That(threadOfAsyncOp).IsEqualTo(capture); + } + + [Test] + public async Task With_task_yield() + { + var capture = Thread.CurrentThread.ManagedThreadId; + + await Task.Yield(); // Task.Yield force to the await operation to start in a different thread. + // https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield + + var threadOfAsyncOp = await DoFakeAsync(); + Check.That(threadOfAsyncOp).IsNotEqualTo(capture); + } + + /// + /// Nothing can ensure under the cover of async modifier, it is really a non blocking operation. + /// + /// + private async Task DoFakeAsync() + { + Thread.Sleep(1); + return Thread.CurrentThread.ManagedThreadId; + } + } +} \ No newline at end of file From 2aa7f09fd525ee0c1005b5515ffe841dc48694e2 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:09:08 +0200 Subject: [PATCH 17/19] sln --- csharp8/ExploreCsharpEight/ExploreCsharpEight.sln | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln index 1d10746..d727a6f 100644 --- a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln +++ b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln @@ -9,10 +9,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight.Tests", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tasks.ConsoleApp", "..\Tasks.Console\Tasks.ConsoleApp.csproj", "{ADBEB077-2B48-4466-84C3-067DE3A50AA4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "..\WebApp\WebApp.csproj", "{704F4867-DC4F-4568-A31D-9E89C321DE8E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApp", "..\WebApp\WebApp.csproj", "{704F4867-DC4F-4568-A31D-9E89C321DE8E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsFormsApp", "..\WindowsFormsApp\WindowsFormsApp.csproj", "{334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingGame.Enemy", "..\CodingGame.Enemy\CodingGame.Enemy.csproj", "{70EEECEA-E3DA-415A-9C1C-0BE6474F4153}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Debug|Any CPU.Build.0 = Debug|Any CPU {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Release|Any CPU.ActiveCfg = Release|Any CPU {334DBDC4-A8A4-4E83-A68B-A8BCDAD8C191}.Release|Any CPU.Build.0 = Release|Any CPU + {70EEECEA-E3DA-415A-9C1C-0BE6474F4153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70EEECEA-E3DA-415A-9C1C-0BE6474F4153}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70EEECEA-E3DA-415A-9C1C-0BE6474F4153}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70EEECEA-E3DA-415A-9C1C-0BE6474F4153}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 12c7b4eed15bbd1a291b7310e964f8cc46f30268 Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:17:01 +0200 Subject: [PATCH 18/19] rename file --- .../{Form1.Designer.cs => FormConfigureAwait.Designer.cs} | 2 +- csharp8/WindowsFormsApp/{Form1.cs => FormConfigureAwait.cs} | 4 ++-- .../WindowsFormsApp/{Form1.resx => FormConfigureAwait.resx} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename csharp8/WindowsFormsApp/{Form1.Designer.cs => FormConfigureAwait.Designer.cs} (96%) rename csharp8/WindowsFormsApp/{Form1.cs => FormConfigureAwait.cs} (92%) rename csharp8/WindowsFormsApp/{Form1.resx => FormConfigureAwait.resx} (100%) diff --git a/csharp8/WindowsFormsApp/Form1.Designer.cs b/csharp8/WindowsFormsApp/FormConfigureAwait.Designer.cs similarity index 96% rename from csharp8/WindowsFormsApp/Form1.Designer.cs rename to csharp8/WindowsFormsApp/FormConfigureAwait.Designer.cs index 4428404..fd7430e 100644 --- a/csharp8/WindowsFormsApp/Form1.Designer.cs +++ b/csharp8/WindowsFormsApp/FormConfigureAwait.Designer.cs @@ -1,6 +1,6 @@ namespace WindowsFormsApp { - partial class Form1 + partial class FormConfigureAwait { /// /// Required designer variable. diff --git a/csharp8/WindowsFormsApp/Form1.cs b/csharp8/WindowsFormsApp/FormConfigureAwait.cs similarity index 92% rename from csharp8/WindowsFormsApp/Form1.cs rename to csharp8/WindowsFormsApp/FormConfigureAwait.cs index b04a753..1ac526b 100644 --- a/csharp8/WindowsFormsApp/Form1.cs +++ b/csharp8/WindowsFormsApp/FormConfigureAwait.cs @@ -7,9 +7,9 @@ namespace WindowsFormsApp { - public partial class Form1 : Form + public partial class FormConfigureAwait : Form { - public Form1() + public FormConfigureAwait() { InitializeComponent(); } diff --git a/csharp8/WindowsFormsApp/Form1.resx b/csharp8/WindowsFormsApp/FormConfigureAwait.resx similarity index 100% rename from csharp8/WindowsFormsApp/Form1.resx rename to csharp8/WindowsFormsApp/FormConfigureAwait.resx From 591cebf22fe8941996756713960e170966a24eca Mon Sep 17 00:00:00 2001 From: Yi HAN Date: Wed, 1 Apr 2020 16:19:03 +0200 Subject: [PATCH 19/19] Try usecase prevent UI to be blocked (fail ) --- .../FormLongRunningInit.Designer.cs | 60 +++++++++ .../WindowsFormsApp/FormLongRunningInit.cs | 45 +++++++ .../WindowsFormsApp/FormLongRunningInit.resx | 120 ++++++++++++++++++ csharp8/WindowsFormsApp/Program.cs | 2 +- .../WindowsFormsApp/WindowsFormsApp.csproj | 19 ++- 5 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 csharp8/WindowsFormsApp/FormLongRunningInit.Designer.cs create mode 100644 csharp8/WindowsFormsApp/FormLongRunningInit.cs create mode 100644 csharp8/WindowsFormsApp/FormLongRunningInit.resx diff --git a/csharp8/WindowsFormsApp/FormLongRunningInit.Designer.cs b/csharp8/WindowsFormsApp/FormLongRunningInit.Designer.cs new file mode 100644 index 0000000..5fb61a8 --- /dev/null +++ b/csharp8/WindowsFormsApp/FormLongRunningInit.Designer.cs @@ -0,0 +1,60 @@ +namespace WindowsFormsApp +{ + partial class FormLongRunningInit + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(145, 123); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(510, 92); + this.button1.TabIndex = 0; + this.button1.Text = "Try click me, u\'ll WAIT."; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // FormLongRunningInit + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.button1); + this.Name = "FormLongRunningInit"; + this.Text = "FormLongRunningInit"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/csharp8/WindowsFormsApp/FormLongRunningInit.cs b/csharp8/WindowsFormsApp/FormLongRunningInit.cs new file mode 100644 index 0000000..933df57 --- /dev/null +++ b/csharp8/WindowsFormsApp/FormLongRunningInit.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WindowsFormsApp +{ + public partial class FormLongRunningInit : Form + { + public FormLongRunningInit() + { + InitializeComponent(); + } + + private int ExecuteFooOnUIThread() + { + Thread.Sleep(TimeSpan.FromSeconds(10)); + return 42; + } + + /// + /// Approved nothing on https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield + /// + private async void button1_Click(object sender, EventArgs e) + { + await Task.Yield(); // Make us async right away + + var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later + + await UseDataAsync(data); + } + + private async Task UseDataAsync(int data) + { + await Task.Delay(1); + MessageBox.Show($"Finish. {data}"); + } + } +} diff --git a/csharp8/WindowsFormsApp/FormLongRunningInit.resx b/csharp8/WindowsFormsApp/FormLongRunningInit.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/csharp8/WindowsFormsApp/FormLongRunningInit.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/csharp8/WindowsFormsApp/Program.cs b/csharp8/WindowsFormsApp/Program.cs index b7d0238..97000ba 100644 --- a/csharp8/WindowsFormsApp/Program.cs +++ b/csharp8/WindowsFormsApp/Program.cs @@ -16,7 +16,7 @@ static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Form1()); + Application.Run(new FormLongRunningInit()); } } } diff --git a/csharp8/WindowsFormsApp/WindowsFormsApp.csproj b/csharp8/WindowsFormsApp/WindowsFormsApp.csproj index b95acd5..127f5fa 100644 --- a/csharp8/WindowsFormsApp/WindowsFormsApp.csproj +++ b/csharp8/WindowsFormsApp/WindowsFormsApp.csproj @@ -46,16 +46,25 @@ - + Form - - Form1.cs + + FormConfigureAwait.cs + + + Form + + + FormLongRunningInit.cs - - Form1.cs + + FormConfigureAwait.cs + + + FormLongRunningInit.cs ResXFileCodeGenerator