Merge branch 'main' into NET-6

This commit is contained in:
Marcello 2021-09-17 23:52:42 +02:00
commit 48000eab9d
5 changed files with 298 additions and 125 deletions

View file

@ -24,7 +24,7 @@ public async Task<TResult> MethodAsync
TResult result = await resultTask;
// if the is no work to be done before awaiting
TResutl result = await obj.OtherMethodAsync();
TResult result = await obj.OtherMethodAsync();
return result;
}
@ -54,7 +54,7 @@ In combination with the `Task.Run` method, async programming is better than `Bac
### Naming Convention
By convention, methods that return commonly awaitable types (for example, `Task`, `Task<T>`, `ValueTask`, `ValueTask<T>`) should have names that end with "Async". Methods that start an asynchronous operation but do not return an awaitable type should not have names that end with "Async", but may start with "Begin", "Start", or some other verb to suggest this method does not return or throw the result of the operation.
By convention, methods that return commonly awaitable types (for example, `Task`, `Task<T>`, `ValueTask`, `ValueTask<T>`) should have names that end with *Async*. Methods that start an asynchronous operation but do not return an awaitable type should not have names that end with *Async*, but may start with "Begin", "Start", or some other verb to suggest this method does not return or throw the result of the operation.
## [Async Return Types](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types)
@ -73,7 +73,7 @@ The `Task<TResult>.Result` property is a **blocking property**. If it's accessed
The `void` return type is used in asynchronous event handlers, which require a `void` return type. For methods other than event handlers that don't return a value, it's best to return a `Task` instead, because an async method that returns `void` can't be awaited. Any caller of such a method must continue to completion without waiting for the called async method to finish. The caller must be independent of any values or exceptions that the async method generates.
The caller of a void-returning async method *can't catch exceptions thrown from the method*, and such unhandled exceptions are likely to cause the application to fail. If a method that returns a `Task` or `Task<TResult>` throws an exception, the exception is stored in the returned task. The exception is rethrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of `Task` or `Task<TResult>` and that calls to the method are awaited.
The caller of a void-returning async method *can't catch exceptions thrown from the method*, and such unhandled exceptions are likely to cause the application to fail. If a method that returns a `Task` or `Task<TResult>` throws an exception, the exception is stored in the returned task. The exception is re-thrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of `Task` or `Task<TResult>` and that calls to the method are awaited.
### Generalized async return types and `ValueTask<TResult>`
@ -86,3 +86,78 @@ Because `Task` and `Task<TResult>` are **reference types**, memory allocation in
### Async streams with `IAsyncEnumerable<T>`
Starting with C# 8.0, an async method may return an async stream, represented by `IAsyncEnumerable<T>`. An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls.
### Async Composition
```cs
public async Task DoOperationsConcurrentlyAsync()
{
Task[] tasks = new Task[3];
tasks[0] = DoOperation0Async();
tasks[1] = DoOperation1Async();
tasks[2] = DoOperation2Async();
// At this point, all three tasks are running at the same time.
// Now, we await them all.
await Task.WhenAll(tasks);
}
public async Task<int> GetFirstToRespondAsync()
{
// Call two web services; take the first response.
Task<int>[] tasks = new[] { WebService1Async(), WebService2Async() };
// Await for the first one to respond.
Task<int> firstTask = await Task.WhenAny(tasks);
// Return the result.
return await firstTask;
}
```
### Context & Avoiding Context
What exactly is the "context"?
- on a UI thread it's a UI context.
- when responding to an ASP.NET request, then it's an ASP.NET request context.
- Otherwise, it's usually a thread pool context.
```cs
private async Task DownloadFileAsync(string fileName)
{
// Use HttpClient or whatever to download the file contents.
var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false);
// Note that because of the ConfigureAwait(false), we are not on the original context here.
// Instead, we're running on the thread pool.
// Write the file contents out to a disk file.
await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false);
// The second call to ConfigureAwait(false) is not *required*, but it is Good Practice.
}
// UI Example
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by the file download.
await DownloadFileAsync(fileNameTextBox.Text);
// Since we resume on the UI context, we can directly access UI elements.
resultTextBox.Text = "File downloaded!";
}
// ASP.NET example
protected async void MyButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the ASP.NET thread is not blocked by the file download.
// This allows the thread to handle other requests while we're waiting.
await DownloadFileAsync(...);
// Since we resume on the ASP.NET context, we can access the current request.
// We may actually be on another *thread*, but we have the same ASP.NET request context.
Response.Write("File downloaded!");
}
```

View file

@ -1,42 +1,5 @@
# Unit Testing
## MSTest
[Microsoft Unit Testing Tutorial](https://docs.microsoft.com/en-us/visualstudio/test/walkthrough-creating-and-running-unit-tests-for-managed-code?view=vs-2019)
To test a project add a **MSTest Test Projet** to the solution.
The test runner will execute any methods marked with `[TestInitialize]` once for every test the class contains, and will do so before running the actual test method itself.
The `[TestMethod]` attribute tells the test runner which methods represent tests.
In `TestClass.cs`:
```cs
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Project.Tests
{
[TestClass]
public class TestClass
{
[TestMethod]
public void TestMethod()
{
Assert.AreEqual(expected, actual);
Assert.IsTrue(bool);
Assert.IsFalse(bool);
Assert.IsNotNull(nullable);
// assertions on collections
CollectionAssert.AreEqual(expexcted, actual),
}
}
}
```
---
[UnitTest Overloaded Methods](https://stackoverflow.com/a/5666591/8319610)
[Naming standards for unit tests](https://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html)
@ -64,3 +27,25 @@ namespace Project.Tests
}
}
```
### Test Setup & Teardown
xUnit.net creates a new instance of the test class for every test that is run, so any code which is placed into the constructor of the test class will be run for every single test.
This makes the constructor a convenient place to put reusable context setup code.
For context cleanup, add the `IDisposable` interface to the test class, and put the cleanup code in the `Dispose()` method.
## Mocking with Moq
```cs
var mockObj = new Mock<MockedType>();
mockObj.Setup(m => m.Method(It.IsAny<InputType>())).Returns(value);
mockObj.Object; // get mock
// check that the invocation is forwarded to the mock, n times
mockObj.Verify(m => m.Method(It.IsAny<InputType>()), Times.Once());
// check that the invocation is forwarded to the mock with a specific input
mockObj.Verify(m => m.Method(input), Times.Once());
```

138
DotNet/Godot/Scripting.md Normal file
View file

@ -0,0 +1,138 @@
# Godot Scripting
## Basics
```cs
using Godot;
public class NodeName : NodeType
{
[Export] // make variable visible in inspector
Type variable = value;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
GetNode("NodeName"); // fetch a child node in the scene
GetNode("ParentNode/ChildNode"); // fetch a child node in the scene
AddToGroup("Group"); // add a node to a group (similar to tags)
GetTree().CallGroup("Group", "Function"); // call Function on all group members
var groupMembers = GetTree().GetNodesInGroup("Group");
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(float delta)
{
}
}
```
### Overridable Functions
```cs
public override void _EnterTree()
{
// When the node enters the Scene Tree, it becomes active and this function is called.
// Children nodes have not entered the active scene yet.
// In general, it's better to use _ready() for most cases.
base._EnterTree();
}
public override void _Ready()
{
// This function is called after _enter_tree, but it ensures
// that all children nodes have also entered the Scene Tree,
// and became active.
base._Ready();
}
public override void _ExitTree()
{
// When the node exits the Scene Tree, this function is called.
// Children nodes have all exited the Scene Tree at this point and all became inactive.
base._ExitTree();
}
public override void _Process(float delta)
{
// This function is called every frame.
base._Process(delta);
}
public override void _PhysicsProcess(float delta)
{
// This is called every physics frame.
base._PhysicsProcess(delta);
}
```
### Creating Nodes
```cs
private Sprite _sprite;
public override void _Ready()
{
base._Ready();
_sprite = new Sprite(); // Create a new sprite
AddChild(_sprite); // Add it as a child of this node
_sprite.Free(); // Immediately removes the node from the scene and frees it.
}
```
**Note**: When a node is freed, it also frees all its child nodes.
The safest way to delete a node is by using `Node.QueueFree()`. This erases the node safely during idle.
### Instantiting Scenes
```cs
// STEP 1: load the scene
var scene = GD.Load<PackedScene>("res://scene.tscn"); // Will load when the script is instanced.
// STEP 2: instantiate the scene-node
var node = scene.Instance();
AddChild(node);
```
The advantage of this two-step process is that a packed scene may be kept loaded and ready to use so that it's possible to create as many instances as desired.
This is especially useful to quickly instance several enemies, bullets, and other entities in the active scene.
## Signals
Signals are Godot's version of the *observer* pattern. They allow a node to send out a message that other nodes can listen for and respond to.
Signals are a way to decouple game objects, which leads to better organized and more manageable code. Instead of forcing game objects to expect other objects to always be present, they can instead emit signals that all interested objects can subscribe to and respond to.
```cs
public override _Ready()
{
GetNode("Node").Connect("signal", targetNode, nameof(TargetFunction)); // connect node and signal
}
// Signal Handler
public void OnEmitterSignal() { }
```
### Custom Signals
```cs
public class Node : Node2D
{
[Signal]
public delegate void CustomSignal(Type arg, ...);
public override void _Ready()
{
EmitSignal(nameof(CustomSignal), args);
}
}
```