Skip to content

Latest commit

 

History

History
205 lines (175 loc) · 4.98 KB

File metadata and controls

205 lines (175 loc) · 4.98 KB

Disposable singleton

To dispose all created singleton instances, simply dispose the composition instance:

using Shouldly;
using Pure.DI;
using static Pure.DI.Lifetime;

DI.Setup(nameof(Composition))
    // Disable Resolve methods to keep the public API minimal
    .Hint(Hint.Resolve, "Off")

    // A realistic example:
    // a submarine has a shared hardware bus to onboard sensors.
    // It should be created once and disposed when the "mission scope"
    // (the composition instance) ends.
    .Bind().As(Singleton).To<AcousticSensorBus>()
    .Bind().To<SubmarineCombatSystem>()
    .Root<ICombatSystem>("CombatSystem");

IAcousticSensorBus bus;
using (var composition = new Composition())
{
    var combatSystem = composition.CombatSystem;

    // Store the singleton instance to verify that it gets disposed
    // when composition is disposed.
    bus = combatSystem.SensorBus;

    // In real usage you would call methods like:
    // combatSystem.ScanForContacts();
}

// When the mission scope ends, all disposable singletons created by it
// must be disposed.
bus.IsDisposed.ShouldBeTrue();

interface IAcousticSensorBus
{
    bool IsDisposed { get; }
}

// Represents a shared connection to submarine sensors (sonar, hydrophones, etc.).
// This is a singleton because the hardware bus is typically a single shared resource,
// and it must be cleaned up properly.
class AcousticSensorBus : IAcousticSensorBus, IDisposable
{
    public bool IsDisposed { get; private set; }

    public void Dispose() => IsDisposed = true;
}

interface ICombatSystem
{
    IAcousticSensorBus SensorBus { get; }
}

// A "combat system" is a typical high-level service that uses shared hardware resources.
class SubmarineCombatSystem(IAcousticSensorBus sensorBus) : ICombatSystem
{
    public IAcousticSensorBus SensorBus { get; } = sensorBus;
}
Running this code sample locally
dotnet --list-sdk
  • Create a net10.0 (or later) console application
dotnet new console -n Sample
dotnet add package Pure.DI
dotnet add package Shouldly
  • Copy the example code into the Program.cs file

You are ready to run the example 🚀

dotnet run

A composition class becomes disposable if it creates at least one disposable singleton instance.

The following partial class will be generated:

partial class Composition: IDisposable
{
#if NET9_0_OR_GREATER
  private readonly Lock _lock = new Lock();
#else
  private readonly Object _lock = new Object();
#endif
  private object[] _disposables = new object[1];
  private int _disposeIndex;

  private AcousticSensorBus? _singletonAcousticSensorBus62;

  public ICombatSystem CombatSystem
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      if (_singletonAcousticSensorBus62 is null)
        lock (_lock)
          if (_singletonAcousticSensorBus62 is null)
          {
            _singletonAcousticSensorBus62 = new AcousticSensorBus();
            _disposables[_disposeIndex++] = _singletonAcousticSensorBus62;
          }

      return new SubmarineCombatSystem(_singletonAcousticSensorBus62);
    }
  }

  public void Dispose()
  {
    int disposeIndex;
    object[] disposables;
    lock (_lock)
    {
      disposeIndex = _disposeIndex;
      _disposeIndex = 0;
      disposables = _disposables;
      _disposables = new object[1];
      _singletonAcousticSensorBus62 = null;
    }

    while (disposeIndex-- > 0)
    {
      switch (disposables[disposeIndex])
      {
        case IDisposable disposableInstance:
          try
          {
            disposableInstance.Dispose();
          }
          catch (Exception exception)
          {
            OnDisposeException(disposableInstance, exception);
          }
          break;
      }
    }
  }

  partial void OnDisposeException<T>(T disposableInstance, Exception exception) where T : IDisposable;
}

Class diagram:

---
 config:
  maxTextSize: 2147483647
  maxEdges: 2147483647
  class:
   hideEmptyMembersBox: true
---
classDiagram
	Composition --|> IDisposable
	AcousticSensorBus --|> IAcousticSensorBus
	SubmarineCombatSystem --|> ICombatSystem
	Composition ..> SubmarineCombatSystem : ICombatSystem CombatSystem
	SubmarineCombatSystem o-- "Singleton" AcousticSensorBus : IAcousticSensorBus
	namespace Pure.DI.UsageTests.Lifetimes.DisposableSingletonScenario {
		class AcousticSensorBus {
				<<class>>
			+AcousticSensorBus()
		}
		class Composition {
		<<partial>>
		+ICombatSystem CombatSystem
		}
		class IAcousticSensorBus {
			<<interface>>
		}
		class ICombatSystem {
			<<interface>>
		}
		class SubmarineCombatSystem {
				<<class>>
			+SubmarineCombatSystem(IAcousticSensorBus sensorBus)
		}
	}
	namespace System {
		class IDisposable {
			<<abstract>>
		}
	}
Loading