mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-23 00:58:48 +00:00
Add DisposeAll(Async) and ClearAndDisposeAll(Async)
This commit is contained in:
parent
7b8c344ddd
commit
adf2281f21
76
X10D.Tests/src/Collections/CollectionTests.cs
Normal file
76
X10D.Tests/src/Collections/CollectionTests.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using X10D.Collections;
|
||||||
|
|
||||||
|
namespace X10D.Tests.Collections;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class CollectionTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void ClearAndDisposeAll_ShouldDispose_GivenCollection()
|
||||||
|
{
|
||||||
|
var collection = new List<Disposable> {new(), new(), new()};
|
||||||
|
var copy = new List<Disposable>(collection);
|
||||||
|
|
||||||
|
collection.ClearAndDisposeAll();
|
||||||
|
|
||||||
|
Assert.IsTrue(copy.All(x => x.IsDisposed));
|
||||||
|
Assert.AreEqual(0, collection.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ClearAndDisposeAllAsync_ShouldDispose_GivenCollection()
|
||||||
|
{
|
||||||
|
var collection = new List<Disposable> {new(), new(), new()};
|
||||||
|
var copy = new List<Disposable>(collection);
|
||||||
|
|
||||||
|
await collection.ClearAndDisposeAllAsync();
|
||||||
|
|
||||||
|
Assert.IsTrue(copy.All(x => x.IsDisposed));
|
||||||
|
Assert.AreEqual(0, collection.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ClearAndDisposeAll_ShouldThrow_GivenNull()
|
||||||
|
{
|
||||||
|
List<Disposable>? collection = null;
|
||||||
|
Assert.ThrowsException<ArgumentNullException>(() => collection!.ClearAndDisposeAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ClearAndDisposeAllAsync_ShouldThrow_GivenNull()
|
||||||
|
{
|
||||||
|
List<Disposable>? collection = null;
|
||||||
|
Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await collection!.ClearAndDisposeAllAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ClearAndDisposeAll_ShouldThrow_GivenReadOnlyCollection()
|
||||||
|
{
|
||||||
|
var collection = new List<Disposable>().AsReadOnly();
|
||||||
|
Assert.ThrowsException<InvalidOperationException>(() => collection.ClearAndDisposeAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ClearAndDisposeAllAsync_ShouldThrow_GivenReadOnlyCollection()
|
||||||
|
{
|
||||||
|
var collection = new List<Disposable>().AsReadOnly();
|
||||||
|
Assert.ThrowsExceptionAsync<InvalidOperationException>(async () => await collection.ClearAndDisposeAllAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Disposable : IDisposable, IAsyncDisposable
|
||||||
|
{
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(IsDisposed = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(IsDisposed = true);
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using X10D.Collections;
|
using X10D.Collections;
|
||||||
|
|
||||||
namespace X10D.Tests.Collections;
|
namespace X10D.Tests.Collections;
|
||||||
@ -6,6 +6,36 @@ namespace X10D.Tests.Collections;
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class EnumerableTests
|
public class EnumerableTests
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void DisposeAll_ShouldDispose_GivenCollection()
|
||||||
|
{
|
||||||
|
var collection = new List<Disposable> {new(), new(), new()};
|
||||||
|
collection.DisposeAll();
|
||||||
|
Assert.IsTrue(collection.All(x => x.IsDisposed));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task DisposeAllAsync_ShouldDispose_GivenCollection()
|
||||||
|
{
|
||||||
|
var collection = new List<Disposable> {new(), new(), new()};
|
||||||
|
await collection.DisposeAllAsync();
|
||||||
|
Assert.IsTrue(collection.All(x => x.IsDisposed));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void DisposeAll_ShouldThrow_GivenNull()
|
||||||
|
{
|
||||||
|
List<Disposable>? collection = null;
|
||||||
|
Assert.ThrowsException<ArgumentNullException>(() => collection!.DisposeAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task DisposeAllAsync_ShouldThrow_GivenNull()
|
||||||
|
{
|
||||||
|
List<Disposable>? collection = null;
|
||||||
|
await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await collection!.DisposeAllAsync());
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void For_ShouldTransform_GivenTransformationDelegate()
|
public void For_ShouldTransform_GivenTransformationDelegate()
|
||||||
{
|
{
|
||||||
@ -86,4 +116,20 @@ public class EnumerableTests
|
|||||||
{
|
{
|
||||||
public int Value { get; set; }
|
public int Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class Disposable : IDisposable, IAsyncDisposable
|
||||||
|
{
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(IsDisposed = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
Assert.IsTrue(IsDisposed = true);
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
77
X10D/src/Collections/CollectionExtensions.cs
Normal file
77
X10D/src/Collections/CollectionExtensions.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
namespace X10D.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection-related extension methods for <see cref="ICollection{T}" />.
|
||||||
|
/// </summary>
|
||||||
|
public static class CollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Calls <see cref="IDisposable.Dispose" /> on each item in the collection, then clears the collection by calling
|
||||||
|
/// <see cref="ICollection{T}.Clear" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The collection to clear, and whose elements should be disposed.</param>
|
||||||
|
/// <typeparam name="T">The type of the elements in <paramref name="source" />.</typeparam>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException"><paramref name="source" /> is read-only.</exception>
|
||||||
|
/// <seealso cref="EnumerableExtensions.DisposeAll{T}" />
|
||||||
|
public static void ClearAndDisposeAll<T>(this ICollection<T> source) where T : IDisposable
|
||||||
|
{
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Collection is read-only. Try using DisposeAll instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T item in source)
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
source.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously calls <see cref="IAsyncDisposable.DisposeAsync" /> on each item in the collection, then clears the
|
||||||
|
/// collection by calling <see cref="ICollection{T}.Clear" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The collection to clear, and whose elements should be disposed.</param>
|
||||||
|
/// <typeparam name="T">The type of the elements in <paramref name="source" />.</typeparam>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException"><paramref name="source" /> is read-only.</exception>
|
||||||
|
/// <seealso cref="EnumerableExtensions.DisposeAllAsync{T}" />
|
||||||
|
public static async Task ClearAndDisposeAllAsync<T>(this ICollection<T> source) where T : IAsyncDisposable
|
||||||
|
{
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Collection is read-only. Try using DisposeAllAsync instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T item in source)
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await item.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
source.Clear();
|
||||||
|
}
|
||||||
|
}
|
@ -75,6 +75,59 @@ public static class EnumerableExtensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls <see cref="IDisposable.Dispose" /> on all elements of the <see cref="IEnumerable{T}" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The enumerable collection whose elements to dispose.</param>
|
||||||
|
/// <typeparam name="T">The type of the elements in <paramref name="source" />.</typeparam>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||||
|
/// <seealso cref="CollectionExtensions.ClearAndDisposeAll{T}" />
|
||||||
|
public static void DisposeAll<T>(this IEnumerable<T> source) where T : IDisposable
|
||||||
|
{
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T item in source)
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously calls <see cref="IAsyncDisposable.DisposeAsync" /> on all elements of the
|
||||||
|
/// <see cref="IEnumerable{T}" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The enumerable collection whose elements to dispose.</param>
|
||||||
|
/// <typeparam name="T">The type of the elements in <paramref name="source" />.</typeparam>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||||
|
/// <seealso cref="CollectionExtensions.ClearAndDisposeAllAsync{T}" />
|
||||||
|
public static async Task DisposeAllAsync<T>(this IEnumerable<T> source) where T : IAsyncDisposable
|
||||||
|
{
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T item in source)
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await item.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reorganizes the elements in an enumerable by implementing a Fisher-Yates shuffle, and returns th shuffled result.
|
/// Reorganizes the elements in an enumerable by implementing a Fisher-Yates shuffle, and returns th shuffled result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user