Skip to content

Instantly share code, notes, and snippets.

@ahancock1
Created May 4, 2020 14:53
Show Gist options
  • Save ahancock1/74d692bb93fa2daa7f375badccc132be to your computer and use it in GitHub Desktop.
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
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