Click here to Skip to main content
15,867,308 members
Articles / General Programming

RAII (Resource Acquisition Is Initialization) C# Helper Classes

Rate me:
Please Sign up or sign in to vote.
3.64/5 (14 votes)
29 Oct 2010CPOL3 min read 44.3K   10   16
Some useful RAII C# helper classes for functionality that does not implement IDisposable

Introduction

What the Heck is RAII?

RAII stands for "Resource Acquisition Is Initialization". It is a programming idiom which ensures that there is no resource leak (like lost memory, open handles, dangling critical section monitors, etc.), even if an exception is thrown. For languages like C#, the lost memory is not of an issue, but the problem of "open handles" and "critical section monitors" is equally important.

Some languages provide a mechanism to deterministically and implicitly call one specific function at the end of the lifetime of an object instance (e.g. the destructor in C++). The RAII idiom in C++ takes benefit of that fact and places the "acquisition" of the resource into the constructor (= initialization), and releases the resource in the destructor.

E.g. the C++ auto pointer as well as any guard or sentry classes are implementing the RAII idiom. For more insight, see RAII Idiom.

In C++, a reporting sentry (RAII class) would log the text in its constructor and log again some text in its destructor (that is called when leaving the function body):

C++
// this is C++ use of an RAII class
void f()
{
   ReportSentry reportSentry("function f");
   ...
}

C# does not know this deterministic and implicit destructor call, that's why I explain how I approach this issue in C#.

RAII in the C# World

Coming from a C++ background, I often did apply the RAII idiom. This was not only used for resources but for any kind of action that required symmetric "open-close", "add-remove", "set-reset", "open tag - close tag", "enter critical section - leave critical section", "report start-of-function - report end-of-function", etc. actions - no matter how the control flow left the current scope.

C# provides the well known IDisposable interface. Each class that implements this interface can be considered as "RAII-ready", i.e., one can use an instance of this class in a...

C#
using (...) { ... }

...statement.

But what to do if you have the need of RAII, but no class at hand for the using (...) { ... } statement?

This article shows some simple helper classes that provide an IDisposable wrapper class for an init and a cleanup delegate.

Of course, this is not at all rocket science - actually, it is very simple. It should help to clear up the mist of exception handling involved and focus on the normal control flow.

Using the Code

First, I show the two usages of RAII versus hand crafted side by side.

With RAII support classesHand crafted
C#
using (var objGuard = new RAIIGuard<int>(
                  () =>objStack.Pop(),
                  (e)=>objStack.Push(e)))
{
    ...
    if (objGuard.Item != 0)
    {
        return;
    }    
    ...
    if (...)
    {
         throw new ...;
    }
    ...
}
C#
int iTaken;
bool bTaken = false;
try
{
    // access the taken object
    iTaken = objStack.Pop();
    bTaken = true; 
    ...
    if (iTaken == 0)
    {
        return;
    }    
    ...
    if (...)
    {
         throw new ...;
    }
    ...
}
finally
{
    if (bTaken)
    {
        objStack.Push(iTaken);
    }
}

I consider the RAII supported approach more legible. If the lambda expressions hurt your eyes, you can always use delegates or methods, e.g.

C#
private int Pop() { return Stack.Pop(); }
private void Push(int i) { Stack.Push(i); }
using (var objGuard = new RAIIGuard<int>(Pop, Push)) 
{
   ...
}

Conveniently, there are two classes to provide that function:

  1. one that has an init action and a cleanup action (e.g. login, logout)
  2. one that has an init function returning an object and a cleanup function taking that object (e.g. login and logout with a session object)
C#
// used for symmetric actions like login, logout
public sealed class RAIIGuard: IDisposable
{
    private Action Cleanup { get; set; }
    public RAIIGuard(Action init, Action cleanup)
    {
        Cleanup = cleanup;
        if (init != null) init();
    }
    void IDisposable.Dispose() { if (Cleanup != null) Cleanup(); }
]
// used for symmetric actions that must pass
// over an object from init to cleanup and that
// need to provide the item to the "using" body
public sealed class RAIIGuard<T>: IDisposable
{
    private Action<T> Cleanup { get; set; }
    public T Item { get; private set; }
    public RAIIGuard(Func<T> init, Action<T> cleanup)
    {
        Cleanup = cleanup;
        Item = (init != null) ? init() : default(T);
    }
    void IDisposable.Dispose() { if (Cleanup != null) Cleanup(Item); }
]

Conclusion

It would still be nice if the committee "guarding" ;-) the C# language could re-consider to implement a native RAII support (e.g. where one can declare a guard as an instance of some kind of "value" type and at the end of the scope, a "Dispose" method is implicitly and deterministically called).

The approach above still requires a certain amount of cooperation by the client where as the pure RAII solution would not need cooperation by the client, i.e., in C#, one can still screw up the RAII idiom by not calling the Dispose() method :-( .

In the meantime, the approach shown above is good enough for me...

History

  • 2010-10-27 Initial version
  • 2010-10-27 Fixed some typos
  • 2010-10-29 Added more background on RAII, explicit implementation of Dispose() (Thanks to Roberto Collina)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder eXternSoft GmbH
Switzerland Switzerland
I feel comfortable on a variety of systems (UNIX, Windows, cross-compiled embedded systems, etc.) in a variety of languages, environments, and tools.
I have a particular affinity to computer language analysis, testing, as well as quality management.

More information about what I do for a living can be found at my LinkedIn Profile and on my company's web page (German only).

Comments and Discussions

 
QuestionA slight modification Pin
TobiasP14-Feb-12 7:19
TobiasP14-Feb-12 7:19 
AnswerRe: A slight modification Pin
Andreas Gieriet14-Feb-12 12:27
professionalAndreas Gieriet14-Feb-12 12:27 
GeneralRe: A slight modification Pin
TobiasP14-Feb-12 23:56
TobiasP14-Feb-12 23:56 
Generalthanks for sharing - have 5 Pin
Pranay Rana17-Jan-11 1:24
professionalPranay Rana17-Jan-11 1:24 
GeneralMy vote of 5 Pin
Wizzard01-Nov-10 18:02
Wizzard01-Nov-10 18:02 
GeneralRAII-guarding fields in vb.net Pin
supercat931-Oct-10 19:54
supercat931-Oct-10 19:54 
GeneralMy vote of 5 Pin
John Brett28-Oct-10 5:21
John Brett28-Oct-10 5:21 
GeneralRe: My vote of 5 Pin
rcollina28-Oct-10 23:42
rcollina28-Oct-10 23:42 
GeneralRe: My vote of 5 Pin
Andreas Gieriet29-Oct-10 0:53
professionalAndreas Gieriet29-Oct-10 0:53 
GeneralMy vote of 1 Pin
#realJSOP28-Oct-10 5:06
mve#realJSOP28-Oct-10 5:06 
GeneralRe: My vote of 1 Pin
Nemanja Trifunovic28-Oct-10 5:10
Nemanja Trifunovic28-Oct-10 5:10 
GeneralRe: My vote of 1 Pin
#realJSOP28-Oct-10 6:17
mve#realJSOP28-Oct-10 6:17 
GeneralRe: My vote of 1 Pin
Nemanja Trifunovic28-Oct-10 6:50
Nemanja Trifunovic28-Oct-10 6:50 
GeneralRe: My vote of 1 Pin
Andreas Gieriet28-Oct-10 22:16
professionalAndreas Gieriet28-Oct-10 22:16 
GeneralRe: My vote of 1 Pin
Andreas Gieriet28-Oct-10 22:23
professionalAndreas Gieriet28-Oct-10 22:23 
GeneralNice... Pin
Nemanja Trifunovic28-Oct-10 4:32
Nemanja Trifunovic28-Oct-10 4:32 
Of course, real destructors would be even nicer...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.