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* diff --git a/Documentation/Revisit.md b/Documentation/Revisit.md new file mode 100644 index 0000000..c28746c --- /dev/null +++ b/Documentation/Revisit.md @@ -0,0 +1,16 @@ +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`? + +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 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 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 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.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 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 diff --git a/csharp8/ExploreCsharpEight.Tests/TasksShould.cs b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs new file mode 100644 index 0000000..79968b3 --- /dev/null +++ b/csharp8/ExploreCsharpEight.Tests/TasksShould.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using NFluent; +using NUnit.Framework; + +namespace ExploreCsharpEight.Tests +{ + [TestFixture] + public class TasksShould + { + [Test] + 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 + } + + [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) => + { + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + Check.That(afterThreadId).IsNotEqualTo(beforeThreadId); // Well they can be different + + + }, null, TaskScheduler.FromCurrentSynchronizationContext()); + + Check.That(Thread.CurrentThread.ManagedThreadId).IsNotEqualTo(beforeThreadId); + Check.That(SynchronizationContext.Current).IsNotEqualTo(captureSyncContext); + Check.That(SynchronizationContext.Current).IsNull(); + } + + [Test] + public async Task Can_not_force_continuation_on_initial_thread() + { + var beforeThreadId = Thread.CurrentThread.ManagedThreadId; + + await Task.Delay(10).ConfigureAwait(continueOnCapturedContext: true); + + var afterThreadId = Thread.CurrentThread.ManagedThreadId; + Check.That(afterThreadId).IsNotEqualTo(beforeThreadId); + } + } +} \ No newline at end of file diff --git a/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln new file mode 100644 index 0000000..d727a6f --- /dev/null +++ b/csharp8/ExploreCsharpEight/ExploreCsharpEight.sln @@ -0,0 +1,55 @@ + +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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExploreCsharpEight.Tests", "..\ExploreCsharpEight.Tests\ExploreCsharpEight.Tests.csproj", "{18F78165-32A4-42DC-90FE-70CBDEB09E97}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tasks.ConsoleApp", "..\Tasks.Console\Tasks.ConsoleApp.csproj", "{ADBEB077-2B48-4466-84C3-067DE3A50AA4}" +EndProject +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 + 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 + {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 + {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 + {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 + {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 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B96EC500-418B-4550-AFE4-6302F967773B} + EndGlobalSection +EndGlobal diff --git a/csharp8/Tasks.Console/Program.cs b/csharp8/Tasks.Console/Program.cs new file mode 100644 index 0000000..13a9b69 --- /dev/null +++ b/csharp8/Tasks.Console/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tasks.ConsoleApp +{ + class Program + { + static async Task Main(string[] args) + { + 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; + + Console.WriteLine($"Before = {beforeThreadId}; Continuation = {afterThreadId}"); + + }, null, TaskScheduler.FromCurrentSynchronizationContext()); + + } + } +} 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 + + + diff --git a/csharp8/WebApp/Controllers/WeatherForecastController.cs b/csharp8/WebApp/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..f1366e7 --- /dev/null +++ b/csharp8/WebApp/Controllers/WeatherForecastController.cs @@ -0,0 +1,67 @@ +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; +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() + { + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); + _logger.LogDebug($"Current sync context = {SynchronizationContext.Current}, isNull = {SynchronizationContext.Current == null}"); + + + var beforeThread = Thread.CurrentThread.ManagedThreadId; + + var continueOnCapturedContext = false; + + var forecastedDays = await Forecast(new WeatherForecast { CapturedInitialThread = beforeThread }) + .ConfigureAwait(continueOnCapturedContext); + + _logger.LogDebug($"{Thread.CurrentThread.ManagedThreadId} - After completed. "); + + + return forecastedDays; + } + + private async Task> Forecast(WeatherForecast weatherForecast) + { + 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.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/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..28a32f5 --- /dev/null +++ b/csharp8/WebApp/Properties/launchSettings.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + + "profiles": { + + "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..495087c --- /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 CapturedInitialThread { get; set; } + + public WeatherForecast With(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..e2026c4 --- /dev/null +++ b/csharp8/WebApp/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "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": "*" +} 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/FormConfigureAwait.Designer.cs b/csharp8/WindowsFormsApp/FormConfigureAwait.Designer.cs new file mode 100644 index 0000000..fd7430e --- /dev/null +++ b/csharp8/WindowsFormsApp/FormConfigureAwait.Designer.cs @@ -0,0 +1,85 @@ +namespace WindowsFormsApp +{ + partial class FormConfigureAwait + { + /// + /// 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.button2 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(108, 24); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(380, 52); + 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; + // + // 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; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // 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"; + this.Text = "Form1"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button2; + } +} + diff --git a/csharp8/WindowsFormsApp/FormConfigureAwait.cs b/csharp8/WindowsFormsApp/FormConfigureAwait.cs new file mode 100644 index 0000000..1ac526b --- /dev/null +++ b/csharp8/WindowsFormsApp/FormConfigureAwait.cs @@ -0,0 +1,56 @@ +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 FormConfigureAwait : Form + { + public FormConfigureAwait() + { + 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(true); // This is default behaviour + // Equivalent to: var hash = await DownloadGoogle() + + 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 }; + + 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()); + } + } +} diff --git a/csharp8/WindowsFormsApp/FormConfigureAwait.resx b/csharp8/WindowsFormsApp/FormConfigureAwait.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/csharp8/WindowsFormsApp/FormConfigureAwait.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/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 new file mode 100644 index 0000000..97000ba --- /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 FormLongRunningInit()); + } + } +} 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..127f5fa --- /dev/null +++ b/csharp8/WindowsFormsApp/WindowsFormsApp.csproj @@ -0,0 +1,92 @@ + + + + + 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 + + + FormConfigureAwait.cs + + + Form + + + FormLongRunningInit.cs + + + + + FormConfigureAwait.cs + + + FormLongRunningInit.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file