Scope Guard Statement in C#

1.8k Views Asked by At

The Resource Acquisition Is Initialization (RAII) idiom and the try-finally statement form the backbone of the traditional approaches to writing exception safe programming.

My question is: Is there something like Scope Guard Statement available on C#?

1

There are 1 best solutions below

5
On BEST ANSWER

There's not a direct translation of the scope guard idiom built into C# or in the BCL, but Alex Rønne Petersen wrote up a blog post [dead link — see below] with a solution that leverages the IDispoable interface and C#'s using statements to do something similar to what you're looking for.


Here is a copy of Alex's blog post found at WebArchive:

Here is the latest code that I wrote for supporting scope guards in C#/.NET. The last version, found here [dead link — used to point to http://xtzgzorex.wordpress.com/2010/05/02/c-scopeguard-classes/], was rather inefficient in that it allocated structs for every single acquisition of a lock. This version tries to solve that issue.

First, here are the internal classes needed to acquire and release locks:

public sealed class MutexScopeGuard : IDisposable
{
    private readonly Mutex _mutex;

    internal MutexScopeGuard(Mutex mutex)
    {
        _mutex = mutex;
    }

    public void Guard()
    {
        _mutex.WaitOne();
    }

    public void Dispose()
    {
        _mutex.ReleaseMutex();
    }
}

public sealed class ReadScopeGuard : IDisposable
{
    private readonly ReaderWriterLockSlim _lock;

    internal ReadScopeGuard(ReaderWriterLockSlim rwlock)
    {
        _lock = rwlock;
    }

    public void Guard()
    {
        _lock.EnterReadLock();
    }

    public void Dispose()
    {
        _lock.ExitReadLock();
    }
}

public sealed class WriteScopeGuard : IDisposable
{
    private readonly ReaderWriterLockSlim _lock;

    internal WriteScopeGuard(ReaderWriterLockSlim rwlock)
    {
        _lock = rwlock;
    }

    public void Guard()
    {
        _lock.EnterWriteLock();
    }

    public void Dispose()
    {
        _lock.ExitWriteLock();
    }
}

And here are the classes for representing the actual lock:

public sealed class ScopedMutex
{
    private readonly Mutex _mutex;

    private readonly MutexScopeGuard _guard;

    public ScopedMutex(bool initiallyOwned = false, string name = null)
    {
        _mutex = new Mutex(initiallyOwned, name);
        _guard = new MutexScopeGuard(_mutex);
    }

    public MutexScopeGuard Guard()
    {
        _guard.Guard();
        return _guard;
    }
}

public sealed class ScopedReadWriteLock
{
    private readonly ReaderWriterLockSlim _lock = new > ReaderWriterLockSlim();

    private readonly ReadScopeGuard _readGuard;

    private readonly WriteScopeGuard _writeGuard;

    public ScopedReadWriteLock()
    {
        _writeGuard = new WriteScopeGuard(_lock);
        _readGuard = new ReadScopeGuard(_lock);
    }

    public ReadScopeGuard GuardRead()
    {
        _readGuard.Guard();
        return _readGuard;
    }

    public WriteScopeGuard GuardWrite()
    {
        _writeGuard.Guard();
        return _writeGuard;
    }
}

Now, what’s different? In terms of usage, you initialize a scope guard like so:

var lock = new ScopedReadWriteLock();
using (lock.GuardRead()
{
    // Read-guarded...
}
using (lock.GuardWrite())
{
    // Write-guarded...
}

This looks pretty magical, but in fact, it’s because of IDisposable that this is possible. As you might know, the C# language provides the using keyword, which takes care of disposing a resource after you’re done using it. This allows me to do quite the dirty hack; if you look above, you’ll notice that the scope guard classes all implement IDisposable, and in the Dispose method, they release the lock. This is not wrong, in itself; however, I keep the scope guard objects alive for as long as the lock object exists. This is, by the design rules of IDisposable, wrong. However, that doesn’t stop me from doing it – it works. Plus, we don’t need to allocate a single thing after we’ve created the lock!

So, long story short: You get scope guards without any memory allocation going on when you acquire/release a lock, but on the other hand, you abuse IDisposable. I personally think it’s an affordable trade-off.