-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathDisposable.cs
147 lines (131 loc) · 5.34 KB
/
Disposable.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using System;
using System.Collections.Generic;
using System.Threading;
using JetBrains.Annotations;
namespace CodeJam
{
/// <summary>Helper methods for <see cref="IDisposable"/></summary>
[PublicAPI]
public static class Disposable
{
#region Nested types
/// <summary>
/// The <see cref="IDisposable"/> implementation with no action on <see cref="Dispose"/>
/// </summary>
public sealed class EmptyDisposable : IDisposable
{
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose() { }
}
/// <summary>
/// The <see cref="IDisposable"/> implementation that calls supplied action on <see cref="Dispose"/>.
/// </summary>
/// DONTTOUCH: DO NOT make it a struct, passing the structure by value will result in multiple Dispose() calls.
/// SEEALSO: https://blogs.msdn.microsoft.com/ericlippert/2011/03/14/to-box-or-not-to-box-that-is-the-question/
private sealed class AnonymousDisposable : IDisposable
{
private Action? _disposeAction;
/// <summary>Initialize instance.</summary>
/// <param name="disposeAction">The dispose action.</param>
public AnonymousDisposable(Action disposeAction) => _disposeAction = disposeAction;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
var disposeAction = Interlocked.Exchange(ref _disposeAction, null);
if (disposeAction != null)
{
try
{
disposeAction.Invoke();
}
catch when (OnException(disposeAction)) { }
}
}
private bool OnException(Action disposeAction)
{
Interlocked.Exchange(ref _disposeAction, disposeAction);
return false;
}
}
/// <summary>
/// The <see cref="IDisposable"/> implementation that calls supplied action on <see cref="Dispose"/>.
/// </summary>
/// <typeparam name="T">Disposable state type.</typeparam>
/// DONTTOUCH: DO NOT make it a struct, passing the structure by value will result in multiple Dispose() calls.
/// SEEALSO: https://blogs.msdn.microsoft.com/ericlippert/2011/03/14/to-box-or-not-to-box-that-is-the-question/
private sealed class AnonymousDisposable<T> : IDisposable
{
private Action<T?>? _disposeAction;
private T? _state;
/// <summary>Initialize instance.</summary>
/// <param name="disposeAction">The dispose action.</param>
/// <param name="state">A value that contains data for the disposal action.</param>
public AnonymousDisposable(Action<T?> disposeAction, T? state)
{
_disposeAction = disposeAction;
_state = state;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
var disposeAction = Interlocked.Exchange(ref _disposeAction, null);
if (disposeAction != null)
{
try
{
disposeAction.Invoke(_state);
_state = default;
}
catch when (OnException(disposeAction)) { }
}
}
private bool OnException(Action<T?> disposeAction)
{
Interlocked.Exchange(ref _disposeAction, disposeAction);
return false;
}
}
#endregion
/// <summary><see cref="IDisposable"/> instance without any code in <see cref="IDisposable.Dispose"/>.</summary>
public static readonly EmptyDisposable Empty = new();
/// <summary>
/// Creates <see cref="IDisposable"/> instance that calls <paramref name="disposeAction"/> on disposing.
/// </summary>
/// <param name="disposeAction">The dispose action.</param>
/// <returns>
/// Instance of <see cref="IDisposable"/> that calls <paramref name="disposeAction"/> on disposing.
/// </returns>
[Pure, System.Diagnostics.Contracts.Pure]
public static IDisposable Create(Action disposeAction) => new AnonymousDisposable(disposeAction);
/// <summary>
/// Creates <see cref="IDisposable"/> instance that calls <paramref name="disposeAction"/> on disposing.
/// </summary>
/// <typeparam name="T">Disposable state type.</typeparam>
/// <param name="disposeAction">The dispose action.</param>
/// <param name="state">A value that contains data for the disposal action.</param>
/// <returns>
/// Instance of <see cref="IDisposable"/> that calls <paramref name="disposeAction"/> on disposing.
/// </returns>
[Pure, System.Diagnostics.Contracts.Pure]
public static IDisposable Create<T>(Action<T?> disposeAction, T? state) =>
new AnonymousDisposable<T>(disposeAction, state);
/// <summary>Combine multiple <see cref="IDisposable"/> instances into single one.</summary>
/// <param name="disposables">The disposables.</param>
/// <returns>Instance of <see cref="IDisposable"/> that will dispose the specified disposables.</returns>
[Pure, System.Diagnostics.Contracts.Pure]
public static IDisposable Merge(params IDisposable[] disposables)
=> Merge((IEnumerable<IDisposable>)disposables);
/// <summary>Combine multiple <see cref="IDisposable"/> instances into single one.</summary>
/// <param name="disposables">The disposables.</param>
/// <returns>Instance of <see cref="IDisposable"/> that will dispose the specified disposables.</returns>
[Pure, System.Diagnostics.Contracts.Pure]
public static IDisposable Merge(this IEnumerable<IDisposable> disposables) =>
Create(disposables.DisposeAll);
}
}