Skip to content

Commit abb2ef0

Browse files
Clean-up warnings around IDisposable pattern and null usages
1 parent 2c8ef8e commit abb2ef0

File tree

4 files changed

+95
-9
lines changed

4 files changed

+95
-9
lines changed

Microsoft.Toolkit.Uwp/Extensions/TypedEventHandlerExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ public static Task InvokeAsync<S, R>(this TypedEventHandler<S, R> eventHandler,
5757

5858
invocationDelegate(sender, eventArgs);
5959

60+
#pragma warning disable CS0618 // Type or member is obsolete
6061
var deferral = eventArgs.GetCurrentDeferralAndReset();
6162

6263
return deferral?.WaitForCompletion(cancellationToken) ?? Task.CompletedTask;
64+
#pragma warning restore CS0618 // Type or member is obsolete
6365
})
6466
.ToArray();
6567

Microsoft.Toolkit/Deferred/DeferredEventArgs.cs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.ComponentModel;
7+
using System.Runtime.CompilerServices;
68

79
namespace Microsoft.Toolkit.Deferred
810
{
911
/// <summary>
1012
/// <see cref="EventArgs"/> which can retrieve a <see cref="EventDeferral"/> in order to process data asynchronously before an <see cref="EventHandler"/> completes and returns to the calling control.
1113
/// </summary>
12-
public class DeferredEventArgs : EventArgs
14+
public class DeferredEventArgs : EventArgs, IDisposable
1315
{
1416
/// <summary>
1517
/// Gets a new <see cref="DeferredEventArgs"/> to use in cases where no <see cref="EventArgs"/> wish to be provided.
@@ -18,7 +20,13 @@ public class DeferredEventArgs : EventArgs
1820

1921
private readonly object _eventDeferralLock = new object();
2022

21-
private EventDeferral _eventDeferral;
23+
private EventDeferral? _eventDeferral;
24+
private bool _disposed;
25+
26+
/// <summary>
27+
/// Finalizes an instance of the <see cref="DeferredEventArgs"/> class.
28+
/// </summary>
29+
~DeferredEventArgs() => Dispose(false);
2230

2331
/// <summary>
2432
/// Returns an <see cref="EventDeferral"/> which can be completed when deferred event is ready to continue.
@@ -32,7 +40,17 @@ public EventDeferral GetDeferral()
3240
}
3341
}
3442

35-
internal EventDeferral GetCurrentDeferralAndReset()
43+
/// <summary>
44+
/// DO NOT USE - This is a support method used by <see cref="EventHandlerExtensions"/>. It is public only for
45+
/// additional usage within extensions for the UWP based TypedEventHandler extensions.
46+
/// </summary>
47+
/// <returns>Internal EventDeferral reference</returns>
48+
#if !NETSTANDARD1_4
49+
[Browsable(false)]
50+
#endif
51+
[EditorBrowsable(EditorBrowsableState.Never)]
52+
[Obsolete("This is an internal only method to be used by EventHandler extension classes, public callers should call GetDeferral() instead.")]
53+
public EventDeferral? GetCurrentDeferralAndReset()
3654
{
3755
lock (_eventDeferralLock)
3856
{
@@ -43,5 +61,31 @@ internal EventDeferral GetCurrentDeferralAndReset()
4361
return eventDeferral;
4462
}
4563
}
64+
65+
/// <summary>
66+
/// Recommended helper method pattern for <see cref="IDisposable"/>.
67+
/// </summary>
68+
/// <param name="disposing">Source of dispose request.</param>
69+
protected virtual void Dispose(bool disposing)
70+
{
71+
if (_disposed)
72+
{
73+
return;
74+
}
75+
76+
_eventDeferral?.Dispose();
77+
78+
_disposed = true;
79+
}
80+
81+
/// <inheritdoc/>
82+
public void Dispose()
83+
{
84+
// Dispose of unmanaged resources.
85+
Dispose(true);
86+
87+
// Suppress finalization.
88+
GC.SuppressFinalize(this);
89+
}
4690
}
47-
}
91+
}

Microsoft.Toolkit/Deferred/EventDeferral.cs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.ComponentModel;
67
using System.Threading;
78
using System.Threading.Tasks;
89

@@ -13,29 +14,66 @@ namespace Microsoft.Toolkit.Deferred
1314
/// </summary>
1415
public class EventDeferral : IDisposable
1516
{
16-
private readonly TaskCompletionSource<object> _taskCompletionSource = new TaskCompletionSource<object>();
17+
//// TODO: If/when .NET 5 is base, we can upgrade to non-generic version
18+
private readonly TaskCompletionSource<object?> _taskCompletionSource = new TaskCompletionSource<object?>();
19+
private bool _disposed = false;
1720

1821
internal EventDeferral()
1922
{
2023
}
2124

25+
/// <summary>
26+
/// Finalizes an instance of the <see cref="EventDeferral"/> class.
27+
/// </summary>
28+
~EventDeferral() => Dispose(false);
29+
2230
/// <summary>
2331
/// Call when finished with the Deferral.
2432
/// </summary>
2533
public void Complete() => _taskCompletionSource.TrySetResult(null);
2634

27-
internal async Task WaitForCompletion(CancellationToken cancellationToken)
35+
/// <summary>
36+
/// Waits for the <see cref="EventDeferral"/> to be completed by the event handler.
37+
/// </summary>
38+
/// <param name="cancellationToken"><see cref="CancellationToken"/>.</param>
39+
/// <returns><see cref="Task"/>.</returns>
40+
#if !NETSTANDARD1_4
41+
[Browsable(false)]
42+
#endif
43+
[EditorBrowsable(EditorBrowsableState.Never)]
44+
[Obsolete("This is an internal only method to be used by EventHandler extension classes, public callers should call GetDeferral() instead on the DeferredEventArgs.")]
45+
public async Task WaitForCompletion(CancellationToken cancellationToken)
2846
{
2947
using (cancellationToken.Register(() => _taskCompletionSource.TrySetCanceled()))
3048
{
3149
await _taskCompletionSource.Task;
3250
}
3351
}
3452

53+
/// <summary>
54+
/// Recommended helper method pattern for <see cref="IDisposable"/>.
55+
/// </summary>
56+
/// <param name="disposing">Source of dispose request.</param>
57+
protected virtual void Dispose(bool disposing)
58+
{
59+
if (_disposed)
60+
{
61+
return;
62+
}
63+
64+
Complete();
65+
66+
_disposed = true;
67+
}
68+
3569
/// <inheritdoc/>
3670
public void Dispose()
3771
{
38-
Complete();
72+
// Dispose of unmanaged resources.
73+
Dispose(true);
74+
75+
// Suppress finalization.
76+
GC.SuppressFinalize(this);
3977
}
4078
}
41-
}
79+
}

Microsoft.Toolkit/Extensions/EventHandlerExtensions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
using System.Threading;
88
using System.Threading.Tasks;
99

10-
namespace Microsoft.Toolkit.Uwp.Deferred
10+
namespace Microsoft.Toolkit.Deferred
1111
{
1212
/// <summary>
1313
/// Extensions to <see cref="EventHandler{TEventArgs}"/> for Deferred Events.
@@ -53,9 +53,11 @@ public static Task InvokeAsync<T>(this EventHandler<T> eventHandler, object send
5353

5454
invocationDelegate(sender, eventArgs);
5555

56+
#pragma warning disable CS0618 // Type or member is obsolete
5657
var deferral = eventArgs.GetCurrentDeferralAndReset();
5758

5859
return deferral?.WaitForCompletion(cancellationToken) ?? Task.CompletedTask;
60+
#pragma warning restore CS0618 // Type or member is obsolete
5961
})
6062
.ToArray();
6163

0 commit comments

Comments
 (0)