The Windows Implementation Library (commonly known as wil) provides a helper called scope_exit which creates and returns an RAII object whose destructor runs the lambda you specify. This helper provides roughly equivalent functionality in C++ to what is called try…finally in other languages such as C#.
// If there is an active primary Gadget, then do a bunch of stuff, // but no matter what, make sure it is no longer active when finished. var gadget = widget.GetActiveGadget(Connection.Primary); if (gadget != null) { try { ⟦ lots of code ⟧ } finally { widget.SetActiveGadget(Connection.Primary, null); } }
One thing that is cumbersome about this pattern is that the cleanup code is far away from the place where the cleanup obligation was created, making it harder to confirm during code review that the proper cleanup did occur. Furthermore, you could imagine that somebody makes a change to the GetActiveGadget() call that requires a matching change to the SetActiveGadget(), but since the SetActiveGadget() is so far away, you may not realize that you need to make a matching change 200 lines later.
var gadget = widget.GetActiveGadget(Connection.Secondary); if (gadget != null) { try { ⟦ lots of code ⟧ } finally { widget.SetActiveGadget(Connection.Secondary, null); } }
Another thing that is cumbersome about this pattern is that you may create multiple obligations at different points in the code execution, resulting in deep nesting.
var gadget = widget.GetActiveGadget(Connection.Secondary); if (gadget != null) { try { ⟦ lots of code ⟧ if (gadget.IsEnabled()) { try { ⟦ lots more code ⟧ } finally { gadget.Disable(); } } } finally { widget.SetActiveGadget(Connection.Secondary, null); } }
Can we get scope_exit ergonomics in C#?
You can do it with the using statement introduced in .NET 8 and a custom class that we may as well call ScopeExit.
public ref struct ScopeExit { public ScopeExit(Action action) { this.action = action; }
public void Dispose()
{
action.Invoke();
}
Action action;
}
Now you can write
var gadget = widget.GetActiveGadget(); if (gadget != null) { using var clearActiveGadget = new ScopeExit(() => widget.SetActiveGadget(null)); ⟦ lots of code ⟧ if (gadget.IsEnabled()) { using var disableGadget = new ScopeExit(() => gadget.Disable()); ⟦ lots more code ⟧ } }
, Although many objects implement IDisposable so that you can clean them up with a using statement, it’s not practical to have a separate method for every possible ad-hoc cleanup that could be needed, and the ScopeExit lets us create bespoke cleanup on demand.¹
I’m adding this to my list of “Crazy C# ideas which never go anywhere,” joining CastTo<T> and As<T> and ThreadSwitcher.
¹ There might be common patterns like “If you Open(), then you probably want to Close()” which could benefit from a disposable-returning method, but you still have to wrap the result.
public ref struct OpenResult { public bool Success { get; init; }
public OpenResult(Widget widget)
{
this.widget = widget;
Success = widget.Open();
}
public void Dispose()
{
if (Success) {
widget.Close();
}
}
private Widget widget;
}
The post C++ has <CODE>scope_exit</CODE> for running code at scope exit. C# says “We have <CODE>scope_exit</CODE> at home.” appeared first on The Old New Thing.
From The Old New Thing via this RSS feed


