Created
May 4, 2020 14:53
-
-
Save ahancock1/74d692bb93fa2daa7f375badccc132be to your computer and use it in GitHub Desktop.
Singleton disposable factory wrapper C# - Maintains reference count and disposes of singleton instance on last reference dispose or instance disposed invoked
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace DisposableInstance | |
{ | |
using System; | |
using System.Threading; | |
// Usage | |
internal class Program | |
{ | |
private static void TestDisposeInstance() | |
{ | |
var instance = new Instance<Test>(() => new Test()); | |
var ref1 = instance.Value; | |
var ref2 = instance.Value; | |
instance.Dispose(); | |
} | |
private static void TestDisposeRefs() | |
{ | |
var instance = new Instance<Test>(() => new Test()); | |
var ref1 = instance.Value; | |
var ref2 = instance.Value; | |
ref1.Dispose(); | |
ref2.Dispose(); | |
} | |
private static void TestUsingInstance() | |
{ | |
using var instance = new Instance<Test>(() => new Test()); | |
var ref1 = instance.Value; | |
var ref2 = instance.Value; | |
} | |
private static void Main(string[] args) | |
{ | |
Console.WriteLine(nameof(TestDisposeInstance)); | |
TestDisposeInstance(); | |
Console.WriteLine(nameof(TestDisposeRefs)); | |
TestDisposeRefs(); | |
Console.WriteLine(nameof(TestUsingInstance)); | |
TestUsingInstance(); | |
Console.ReadKey(); | |
} | |
} | |
public abstract class Disposable : IDisposable | |
{ | |
protected bool Disposed { get; private set; } | |
public void Dispose() | |
{ | |
Dispose(Disposed = true); | |
GC.SuppressFinalize(this); | |
} | |
protected void ThrowIfDisposed(string name) | |
{ | |
if (Disposed) | |
{ | |
throw new ObjectDisposedException(name); | |
} | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
} | |
} | |
public interface IInstance<T> where T : IInstance<T>, IDisposable | |
{ | |
void SetInstance(Instance<T> instance); | |
} | |
public abstract class Disposable<T> : IDisposable, IInstance<T> | |
where T : IDisposable, IInstance<T> | |
{ | |
private Instance<T> _instance; | |
void IInstance<T>.SetInstance(Instance<T> instance) | |
{ | |
_instance = instance; | |
} | |
protected bool Disposed { get; private set; } | |
public void Dispose() | |
{ | |
_instance?.Release(); | |
if (_instance?.CanDispose() ?? true) | |
{ | |
Dispose(Disposed = true); | |
GC.SuppressFinalize(this); | |
} | |
} | |
protected void ThrowIfDisposed(string name) | |
{ | |
if (Disposed) | |
{ | |
throw new ObjectDisposedException(name); | |
} | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
} | |
} | |
public class Instance<T> : Disposable where T : IInstance<T>, IDisposable | |
{ | |
private readonly Lazy<T> _instance; | |
private volatile int _count; | |
public Instance(Func<T> factory) | |
{ | |
_instance = new Lazy<T>(() => CreateInstance(factory)); | |
} | |
public T Value | |
{ | |
get | |
{ | |
var instance = _instance.Value; | |
Interlocked.Increment(ref _count); | |
return instance; | |
} | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
if (disposing) | |
{ | |
Interlocked.Add(ref _count, -_count); | |
_instance.Value?.Dispose(); | |
} | |
} | |
private T CreateInstance(Func<T> factory) | |
{ | |
if (factory == null) | |
{ | |
return default; | |
} | |
var instance = (IInstance<T>) factory.Invoke(); | |
instance.SetInstance(this); | |
return (T) instance; | |
} | |
public void Release() | |
{ | |
Interlocked.Decrement(ref _count); | |
} | |
public bool CanDispose() | |
{ | |
return _count <= 0; | |
} | |
} | |
public class Test : Disposable<Test> | |
{ | |
protected override void Dispose(bool disposing) | |
{ | |
Console.WriteLine("Disposed"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment