🎮 Playing with C# `using`

Re-wiring the C# `using` statement for some cool and helpful use-cases.

🎮 Playing with C# `using`
C# using statement

The obvious, well known, purpose of using is to mitigate memory leaks by ensuring the call to .Dispose() on IDisposable resources.

But it's basically a sugar syntax for:

Resource r = new Resource(); //Resource is IDisposable 
try
{
  [...]//do stuff with resource
}
finally
{
  if (r != null) r.Dispose();
}

vs.

using(Resource r = new Resource())
{
  [...]//do stuff with resource
}

Therefore, even though IDisposable is intended for housekeeping unmanaged resources, who says we can't use it to our advantage for other use cases?

Such as:

  • Setting component states for the scope of a working block;
  • Measuring execution duration of a given scope;
  • Logging entry and exit points for a given scope;
  • Refreshing UIs with respect to code execution;
  • Blocking user actions (e.g.: Button taps) during action execution;
  • Etc.

To do so, you need to create a concrete class that implements IDisposable and its constructor will be the entry point while the .Dispose() will be the exit point.

Like so (this is already part of H.Necessaire as ScopedRunner):

public class ScopedRunner : IDisposable
{
    readonly Action onStart;
    readonly Action onStop;

    public ScopedRunner(Action onStart, Action onStop)
    {
        this.onStart = onStart;
        this.onStop = onStop;

        DoStart();
    }

    public void Dispose()
    {
        DoStop();
    }

    private void DoStart()
    {
        if (onStart == null)
            return;

        onStart.TryOrFailWithGrace();
    }

    private void DoStop()
    {
        if (onStop == null)
            return;

        onStop.TryOrFailWithGrace();
    }
}

And usage:

using (new ScopedRunner(
    onStart: () => appContainer.Hide(),
    onStop: () => appContainer.Show()
    ))
{
    await Get<AuthenticationManager>().RestoreSecurityContextIfPossible();
    Navi.GoHome();
}

... or as TimeMeasurement for stopwatch-ing an execution block:

using (new TimeMeasurement(async x => await logger.LogDebug($"DONE Running Dashboard Refresh Cycle in {x}")))
{
    await
        Task.WhenAll(
            dashboardRefreshers
            .Select(SafelyRunDashboardRefresher)
            .ToArray()
        );
}

What do you think about this pattern? Yay 👍 or Nay 👎 ?