Skip to content

Pattern-based using and disposable ref structs C# 8.0performancenew scenarios

Support using statements for types that offer a Dispose method without implementing IDisposable.

A using statement required the type to implement IDisposable. That was a problem for ref struct C# 7.2 types such as Span<T>, which cannot implement an interface at all - implementing IDisposable would make them boxable, which defeats the purpose of a stack-only type.

C# 8.0 adds pattern-based disposal. If a type has an accessible Dispose() method that returns void, a using statement will call it, with no interface involved.

This is what lets a ref struct own stack storage and still get deterministic cleanup while staying on the stack.

Code ​

csharp
ref struct Arena
{
    private Span<byte> _buffer;

    public Arena(Span<byte> buffer, int size) => _buffer = buffer[..size];

    public Span<byte> Rent(int count) => _buffer[..count];

    public void Dispose() => _buffer.Clear(); // No interface needed
}

static void Work()
{
    using var arena = new Arena(stackalloc byte[1024], 1024);
    var slice = arena.Rent(16);
    // Dispose clears the buffer at the end of the method
}
csharp
ref struct Arena
{
    private Span<byte> _buffer;

    public Arena(Span<byte> buffer, int size) => _buffer = buffer[..size];

    public Span<byte> Rent(int count) => _buffer[..count];

    public void Clear() => _buffer.Clear();
}

static void Work()
{
    var arena = new Arena(stackalloc byte[1024], 1024);
    try
    {
        var slice = arena.Rent(16);
    }
    finally
    {
        arena.Clear(); // Called by hand, easy to forget
    }
}

Notes ​

  • The Dispose method must be accessible and return void; it cannot return bool, and an explicit interface implementation does not count because it cannot be called by name
  • Works with both the using statement and the using declaration C# 8.0 form shown above
  • No interface is involved, so the call binds statically and does not box or dispatch - the same reason a ref struct could not implement IDisposable in the first place
  • It also lets a class offer a cleanup hook without committing to the IDisposable contract, which is useful for a type that only sometimes owns a resource
  • Types that implement IDisposable are unaffected; this is an additional path, not a replacement

More information ​

Powered by Intracia