From b8791c89c96fa93326092f7ca7be9048840bbc45 Mon Sep 17 00:00:00 2001 From: Nikolai Date: Wed, 6 Mar 2024 17:00:33 +0100 Subject: [PATCH] Add overloads for custom disposable creation method. --- .../DisposableExtensionsTests.cs | 2 +- CodeJam.Main.Tests/DisposableTests.cs | 83 +++++++++++++++- CodeJam.Main/Disposable.cs | 97 ++++++++++++++++++- 3 files changed, 175 insertions(+), 7 deletions(-) diff --git a/CodeJam.Main.Tests/DisposableExtensionsTests.cs b/CodeJam.Main.Tests/DisposableExtensionsTests.cs index 86d68457..967852f7 100644 --- a/CodeJam.Main.Tests/DisposableExtensionsTests.cs +++ b/CodeJam.Main.Tests/DisposableExtensionsTests.cs @@ -61,7 +61,7 @@ public static void DisposeAllMustCollectAllExceptions() #if NETSTANDARD21_OR_GREATER || NETCOREAPP30_OR_GREATER [Test] - public static async Task DisposeAsyncMustCallDiposeOnce() + public static async Task DisposeAsyncMustCallDisposeOnce() { const int expectedDisposeCount = 1; diff --git a/CodeJam.Main.Tests/DisposableTests.cs b/CodeJam.Main.Tests/DisposableTests.cs index 8d00a349..cc09c0d8 100644 --- a/CodeJam.Main.Tests/DisposableTests.cs +++ b/CodeJam.Main.Tests/DisposableTests.cs @@ -115,7 +115,7 @@ public static void TestParameterizedAnonymousDisposable() var state = ""; var disposed = false; - using (Disposable.Create(s => {disposed = true; state = s;}, "state")) + using (Disposable.Create(s => { disposed = true; state = s; }, "state")) { } @@ -180,7 +180,7 @@ public static void InitDisposeTest4() s => { Assert.That(++i, Is.EqualTo(3)); - Assert.That(s, Is.EqualTo("123")); + Assert.That(s, Is.EqualTo("123")); })) { Assert.That(++i, Is.EqualTo(2)); @@ -188,5 +188,82 @@ public static void InitDisposeTest4() Assert.That(++i, Is.EqualTo(4)); } + + [Test] + public static void CustomDisposeMustProcessSingleEntity() + { + const int expectedInitializeCount = 1; + const int expectedUseCount = 1; + const int expectedDestroyCount = 1; + + int actualInitializeCount = 0; + int actualUseCount = 0; + int actualDisposeCount = 0; + + var rawTestObject = new SomeTestableEntity( + () => ++actualInitializeCount, + () => ++actualUseCount, + () => ++actualDisposeCount); + + var wrappedTestObject = Disposable.Create( + () => { rawTestObject.Initialize(); return rawTestObject; }, + innerTestableEntity => innerTestableEntity.Destroy()); + + Assert.DoesNotThrow(wrappedTestObject.Entity.Use); + Assert.DoesNotThrow(wrappedTestObject.Dispose); + Assert.Throws(() => wrappedTestObject.Entity.Use()); + + Assert.AreEqual(expectedInitializeCount, actualInitializeCount); + Assert.AreEqual(expectedUseCount, actualUseCount); + Assert.AreEqual(expectedDestroyCount, actualDisposeCount); + } + + [Test] + public static void CustomDisposeMustProcessMultipleEntities() + { + const int expectedEntitiesCount = 10; + const int expectedInitializeCount = 10; + const int expectedDestroyCount = 10; + + int actualEntitiesCount = 0; + int actualInitializeCount = 0; + int actualDisposeCount = 0; + + var create = () => new SomeTestableEntity( + () => ++actualInitializeCount, + () => { }, + () => ++actualDisposeCount); + + var wrappedTestObject = Disposable.Create( + () => Enumerable.Range(0, expectedEntitiesCount), + index => { var rawTestObject = create(); rawTestObject.Initialize(); return rawTestObject; }, + innerTestableEntity => innerTestableEntity.Destroy()); + + Assert.DoesNotThrow(() => actualEntitiesCount = wrappedTestObject.Entities.Count()); + Assert.DoesNotThrow(wrappedTestObject.Dispose); + Assert.Throws(() => actualEntitiesCount = wrappedTestObject.Entities.Count()); + + Assert.AreEqual(expectedEntitiesCount, actualEntitiesCount); + Assert.AreEqual(expectedInitializeCount, actualInitializeCount); + Assert.AreEqual(expectedDestroyCount, actualDisposeCount); + } + + private class SomeTestableEntity + { + private readonly Action _initialize; + private readonly Action _use; + private readonly Action _destroy; + + public SomeTestableEntity(Action initialize, Action use, Action destroy) + { + _initialize = initialize; + _use = use; + _destroy = destroy; + } + + public void Initialize() => _initialize(); + public void Use() => _use(); + public void Destroy() => _destroy(); + } } -} +} \ No newline at end of file diff --git a/CodeJam.Main/Disposable.cs b/CodeJam.Main/Disposable.cs index af1da102..12df7427 100644 --- a/CodeJam.Main/Disposable.cs +++ b/CodeJam.Main/Disposable.cs @@ -1,9 +1,10 @@ -using System; +using JetBrains.Annotations; + +using System; using System.Collections.Generic; +using System.Linq; using System.Threading; -using JetBrains.Annotations; - namespace CodeJam { /// Helper methods for @@ -102,6 +103,63 @@ private bool OnException(Action disposeAction) return false; } } + + /// + /// Disposable wrapper for single object. + /// + /// Type of wrapped object that needed to be deinitialized. + public class CustomDisposable : IDisposable + { + private readonly Action _destroyer; + + private bool _wasDisposed = false; + + private readonly T _entity; + + public T Entity => (_wasDisposed ? throw new ObjectDisposedException(nameof(_entity)) : _entity); + + public CustomDisposable(T entity, Action destroyer) + { + _entity = entity; + _destroyer = destroyer; + } + + public void Dispose() + { + if (_wasDisposed) return; else _wasDisposed = true; + _destroyer(_entity); + } + } + + /// + /// Disposable wrapper for multiple objects. + /// + /// Type of wrapped objects collection, in which each element must be deinitialized. + /// Type of element of wrapped objects collection. + public class CustomDisposable : IDisposable + where TE : IEnumerable + { + + private readonly Action _destroyer; + + private bool _wasDisposed = false; + + private readonly TE _entities; + + public TE Entities => (_wasDisposed ? throw new ObjectDisposedException(nameof(_entities)) : _entities); + + public CustomDisposable(TE entities, Action destroyer) + { + _entities = entities; + _destroyer = destroyer; + } + + public void Dispose() + { + if (_wasDisposed) return; else _wasDisposed = true; + _entities.Select(e => (IDisposable)new CustomDisposable(e, _destroyer)).DisposeAll(); + } + } #endregion /// instance without any code in . @@ -130,6 +188,39 @@ private bool OnException(Action disposeAction) public static IDisposable Create(Action disposeAction, T? state) => new AnonymousDisposable(disposeAction, state); + /// + /// Creates disposable wrapper for single object. + /// + /// Type of wrapped object that needed to be deinitialized. + /// Used at place immediately to initialize internal entity inside returning disposable wrapper. + /// Used during internal entity deinitialization as an action of the dispose method. + /// Created disposable wrapper of single object. + public static CustomDisposable Create(Func creator, Action destroyer) => + new(creator(), destroyer); + + /// + /// Creates disposable wrapper for multiple objects. + /// + /// Type of wrapped objects collection, in which each element must be deinitialized. + /// Type of element of wrapped objects collection. + /// Used at place immediately to initialize internal entities inside returning disposable wrapper. + /// Used during each internal entity deinitialization as an action of the dispose method. + /// Created disposable wrapper of multiple objects. + public static CustomDisposable Create(Func creator, Action destroyer) where TE : IEnumerable => + new(creator(), destroyer); + + /// + /// Creates disposable wrapper for multiple objects. + /// + /// Type of wrapped objects collection, in which each element must be deinitialized. + /// Type of element of wrapped objects collection. + /// Used at place immediately to get source collection for internal entities initialize enumeration. + /// Used at place immediately to initialize each internal entity inside returning disposable wrapper. + /// Used during each internal entity deinitialization as an action of the dispose method. + /// Created disposable wrapper of multiple objects. + public static CustomDisposable, T> Create(Func> counter, Func creator, Action destroyer) => + new(counter().Select(creator).ToArray(), destroyer); + /// Combine multiple instances into single one. /// The disposables. /// Instance of that will dispose the specified disposables.