Skip to content

Commit d595600

Browse files
committed
AsyncDisposable Demo
1 parent 3f9724d commit d595600

File tree

8 files changed

+440
-1
lines changed

8 files changed

+440
-1
lines changed

.vscode/launch.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
// Use IntelliSense to find out which attributes exist for C# debugging
3+
// Use hover for the description of the existing attributes
4+
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": ".NET Core Launch (console)",
9+
"type": "coreclr",
10+
"request": "launch",
11+
"preLaunchTask": "build",
12+
// If you have changed target frameworks, make sure to update the program path.
13+
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Async.Netcore.dll",
14+
"args": [],
15+
"cwd": "${workspaceFolder}",
16+
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
17+
"console": "internalConsole",
18+
"stopAtEntry": false
19+
},
20+
{
21+
"name": ".NET Core Attach",
22+
"type": "coreclr",
23+
"request": "attach",
24+
"processId": "${command:pickProcess}"
25+
}
26+
]
27+
}

.vscode/tasks.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/Async.Netcore.csproj"
11+
],
12+
"problemMatcher": "$tsc"
13+
},
14+
{
15+
"label": "publish",
16+
"command": "dotnet",
17+
"type": "process",
18+
"args": [
19+
"publish",
20+
"${workspaceFolder}/Async.Netcore.csproj"
21+
],
22+
"problemMatcher": "$tsc"
23+
},
24+
{
25+
"label": "watch",
26+
"command": "dotnet",
27+
"type": "process",
28+
"args": [
29+
"watch",
30+
"run",
31+
"${workspaceFolder}/Async.Netcore.csproj"
32+
],
33+
"problemMatcher": "$tsc"
34+
}
35+
]
36+
}

Async.Netcore.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.0</TargetFramework>
6+
<LangVersion>8.0</LangVersion>
7+
</PropertyGroup>
8+
9+
</Project>

AsyncDisposable.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
5+
class AsyncDisposable : IRunnable
6+
{
7+
public async Task Run()
8+
{
9+
using (var wrong = new WrongDisposable())
10+
{
11+
}
12+
13+
await using (var correct = new CorrectDisposable())
14+
{
15+
}
16+
}
17+
18+
class WrongDisposable : BaseForAsyncDisposable, IDisposable
19+
{
20+
public void Dispose()
21+
{
22+
Log();
23+
24+
// sync over async anti-pattern
25+
stream.FlushAsync().GetAwaiter().GetResult();
26+
stream.Dispose();
27+
}
28+
}
29+
30+
class CorrectDisposable : BaseForAsyncDisposable, IAsyncDisposable, IDisposable
31+
{
32+
public async ValueTask DisposeAsync()
33+
{
34+
Log();
35+
36+
await stream.FlushAsync().ConfigureAwait(false);
37+
stream.Dispose();
38+
// or
39+
await stream.DisposeAsync().ConfigureAwait(false);
40+
}
41+
42+
public void Dispose()
43+
{
44+
Log();
45+
46+
stream.Flush();
47+
stream.Dispose();
48+
}
49+
}
50+
}

AsyncDisposableExtensions.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System;
2+
using System.IO;
3+
using System.Runtime.CompilerServices;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
static class AsyncDisposableExtensions
8+
{
9+
public static void Explain(this AsyncDisposable runnable, TextWriter writer)
10+
{
11+
writer.WriteLine(@"
12+
- `Stream`, `Utf8JsonWriter`, `System.Threading.Timer`, `CancellationTokenRegistration`, `BinaryWriter`, `TextWriter` and `IAsyncEnumerator<T>` implement `IAsyncDisposable`
13+
- `Stream`, `BinaryWriter`, `TextWriter` calls `.Dispose` synchronously
14+
- `Stream` `FlushAsync` calls `Flush` on another thread which is bad behavior that should be overwritten
15+
");
16+
}
17+
}
18+
19+
class BaseForAsyncDisposable
20+
{
21+
protected Stream stream;
22+
23+
protected BaseForAsyncDisposable()
24+
{
25+
stream = new FakeStream();
26+
}
27+
28+
protected void Log([CallerMemberName] string member = null)
29+
{
30+
Console.WriteLine($"{GetType().Name}.{member}()");
31+
}
32+
33+
class FakeStream : Stream
34+
{
35+
public override bool CanRead => throw new NotImplementedException();
36+
37+
public override bool CanSeek => throw new NotImplementedException();
38+
39+
public override bool CanWrite => throw new NotImplementedException();
40+
41+
public override long Length => throw new NotImplementedException();
42+
43+
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
44+
45+
public override void Flush()
46+
{
47+
Thread.Sleep(1000);
48+
}
49+
50+
public override async Task FlushAsync(System.Threading.CancellationToken cancellationToken)
51+
{
52+
await Task.Delay(1000);
53+
}
54+
55+
public override int Read(byte[] buffer, int offset, int count)
56+
{
57+
throw new NotImplementedException();
58+
}
59+
60+
public override long Seek(long offset, SeekOrigin origin)
61+
{
62+
throw new NotImplementedException();
63+
}
64+
65+
public override void SetLength(long value)
66+
{
67+
throw new NotImplementedException();
68+
}
69+
70+
public override void Write(byte[] buffer, int offset, int count)
71+
{
72+
throw new NotImplementedException();
73+
}
74+
}
75+
}

IRunnable.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
using System.Threading.Tasks;
2+
3+
interface IRunnable
4+
{
5+
Task Run();
6+
}

0 commit comments

Comments
 (0)