Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C#: Scope Your Global State Changes with IDisposable and the Using Statement

0.00/5 (No votes)
5 Jun 2013 1  
How to scope your global state changes with IDisposable and the using statement

Introduction

Sometimes we need to modify some global state, but when we do that, it’s critical to ensure we leave things as we’ve found them; you know like when you lift the toilet seat: forgetting to put it down afterwards could get you in trouble!
It’s similar in programming and you need to use the right tools and practices.

If you regularly work with C#, it’s very likely you’ve already used the IDisposable interface along with the using statement, two tools that help you scope the use of a resource.

In this article, I’ll show you how they can be used to scope global state changes in a fluent manner, a pattern I’ve used for years to enhance reusability and readability of this kind of code.
In the first part, I’ll illustrate this pattern with a small and hopefully fun example, and then in the second part, I’ll describe how it can be applied to other situations like culture settings and Excel development.

The source code for the article is available here as a Visual Studio solution: .

Playing with Console Colors

Let’s say you want to add some fancy colors to your console applications for serious purposes like highlighting some part of the output (e.g. for logs: red for errors, yellow for warnings…) or just for fun because life is better with colors.
The Console.ForegroundColor (and Console.BackgroundColor) property is the way to go…

Using the “All Will Be Fine”© Pattern

So here is a colorful console application (BadTrip.cs):

using System;

class BadTrip
{
	static readonly ConsoleColor[] colors = { ConsoleColor.Magenta, 
	ConsoleColor.Blue, ConsoleColor.Cyan, ConsoleColor.Green, 
	ConsoleColor.Yellow, ConsoleColor.Red };

	static void DisplayRainbow(string text)
	{
		ConsoleColor originalColor = Console.ForegroundColor;
	
		int colorIndex = 0;
		for (int i = 0; i <= text.Length; ++i)
		{
			Console.ForegroundColor = colors[colorIndex++ % colors.Length];
			
			Console.Write(text[i]);
		}
		
		Console.ForegroundColor = originalColor;
	}

	static void Main()
	{
		DisplayRainbow(@"Woooooooow ... rainbows everywhere ... and a unicorn O_o o_O !!!
                                                    /
                                                  .7
                                       \       , //
                                       |\.--._/|//
                                      /\ ) ) ).'/
                                     /(  \  // /
                                    /(   J`((_/ \
                                   / ) | _\     /
                                  /|)  \  eJ    L
                                 |  \ L \   L   L
                                /  \  J  `. J   L
                                |  )   L   \/   \
                               /  \    J   (\   /
             _....___         |  \      \   \```
      ,.._.-'        '''--...-||\     -. \   \
    .'.=.'                    `         `.\ [ Y
   /   /                                  \]  J
  Y / Y                                    Y   L
  | | |          \                         |   L
  | | |           Y                        A  J
  |   I           |                       /I\ /
  |    \          I             \        ( |]/|
  J     \         /._           /        -tI/ |
   L     )       /   /'-------'J           `'-:.
   J   .'      ,'  ,' ,     \   `'-.__          \
    \ T      ,'  ,'   )\    /|        ';'---7   /
     \|    ,'L  Y...-' / _.' /         \   /   /
      J   Y  |  J    .'-'   /         ,--.(   /
       L  |  J   L -'     .'         /  |    /\
       |  J.  L  J     .-;.-/       |    \ .' /
       J   L`-J   L____,.-'`        |  _.-'   |
        L  J   L  J                  ``  J    |
        J   L  |   L                     J    |
         L  J  L    \                    L    \
         |   L  ) _.'\                    ) _.'\
         L    \('`    \                  ('`    \
          ) _.'\`-....'                   `-....'
         ('`    \
          `-.___/   sk");
	}
}

For those who’d like to ask, unicorn is from here: http://www.ascii-art.de/ascii/uvw/unicorn.txt.

Here is what we obtain:

A bad trip

A very bad trip and code.

We can notice 3 things:

  • It really ends badly with an IndexOutOfBoundException.
  • The exception output is colorful, while it may not have been expected it’s not really annoying.
  • The console is now completely magenta for any subsequent output: first it’s ugly, but more importantly it seems like our program has messed up some global state outside of the application scope and this time this is truly annoying.

Let’s try to understand what happens.

The root cause is:

for (int i = 0; i <= text.Length; ++i)

<=” instead of “<“, what a noob, really!

Ok, this explains the exception but not the aggressive repainting.

Let’s simplify the code :

static void DisplayRainbow(string text)
{
	ConsoleColor originalColor = Console.ForegroundColor;

	// ...
	
	Console.ForegroundColor = originalColor;
}

So we correctly take care of reverting the console color to its original value, but if the code in the ellipsis throws an exception, the function evaluation will be stopped right there in the middle of nowhere without executing the reverting instructions, oops!

Using the Try/Finally Pattern

So we want to ensure that, whatever happens, the console color is reverted to its previous value before we exit our program.
There is a construction for that kind of situation: the finally block which is guaranteed to execute even in case of exception.
So we will wrap our dangerous code within a try/finally construct; and here is our second try:

static void DisplayRainbow(string text)
{
	ConsoleColor originalColor = Console.ForegroundColor;

	try
	{
		int colorIndex = 0;
		for (int i = 0; i <= text.Length; ++i)
		{
			Console.ForegroundColor = colors[colorIndex++ % colors.Length];
			
			Console.Write(text[i]);
		}
	}
	finally
	{
		Console.ForegroundColor = originalColor;
	}
}

Here is the result:

try/finally to the rescue for a happy ending

try/finally to the rescue for a happy ending

Hey, we’ve managed to contain our mess within the boundaries of our application.
The code is quite simple but adds only a pipe, it is not really reusable and it lacks some semantics.
Let’s see how we can enhance our implementation.

Some Tools: The IDisposable Interface and the Using Statement

First a small reminder: .NET comes with the disposable pattern: you use a resource and when you don’t need it anymore, you release it as soon as possible to avoid holding it longer than necessary.
To show the users of a class it represents a disposable resource that should be released once not needed anymore, you implement the IDisposable interface.

Then code that uses this class typically does something like:

// Get the resource
IDisposable resource = new Resource();

try
{
	// Use the resource
	resource.DoSomething();
	resource.DoAnotherSomething();
}
finally
{
	// Not needed anymore
	resource.Dispose();
}

This kind of use-case is so common that the C# language has a built-in construct for it, the using statement which ensures the “Dispose” method is called implicitly:

// Get the resource
using (IDisposable resource = new Resource())
{
	// Use the resource
	resource.DoSomething();
	resource.DoAnotherSomething();
}
// Not needed anymore: the Dispose method is invoked automatically

With the using statement code is shorter, more readable, scoping is obvious, and in more complex cases it can avoid a lot of nested try/finally blocks.

Wrapping Things Up

So we now have all the tools and knowledge to go one step further.
We know that we can create an object and scope its use with the using statement, and that at the end of the scope, its Dispose method will be called automatically.
Now consider if the object does not represent a resource but rather a state controller:

  • in the constructor, we can change some value
  • in the Dispose method, we can revert the value to its original state

Here is an application of this pattern to our console colors case:

public class Color : IDisposable
{
	private ConsoleColor originalForeground;

	public static Color Red { get { return new Color(ConsoleColor.Red); } }
	public static Color Green { get { return new Color(ConsoleColor.Green); } }
	public static Color Blue { get { return new Color(ConsoleColor.Blue); } }

	public Color(ConsoleColor foreground)
	{
		originalForeground = Console.ForegroundColor;

		// change the console color
		Console.ForegroundColor = foreground;
	}

	public void Dispose()
	{
		// revert the console color
		Console.ForegroundColor = originalForeground;
	}
}

And this is how our “DisplayRainbow” method looks like after refactoring:

static void DisplayRainbow(string text)
{
	int colorIndex = 0;
	for (int i = 0; i <= text.Length; ++i)
	{
		using (new Color(colors[colorIndex++ % colors.Length]))
		{
			Console.Write(text[i]);
		}
	}
}

Not a revolution but a little cleaner, and above all the logic of setting and reverting the color has been encapsulated in a reusable component.

(Note that this implementation is not completely equivalent to the one using “finally” because in the later the color is reverted only once, but what is important is the concept.)

Moreover you may have noticed the static properties, their role is to make the use of the class even more fluent.
Here is a test-case (asserting that the disposable console colors class is working as expected) that shows how they can be used:

public void CanScopeConsoleColorChange()
{
	Assert.AreEqual(ConsoleColor.Gray, Console.ForegroundColor);

	Console.WriteLine("Gray");

	using (Color.Red)
	{
		Assert.AreEqual(ConsoleColor.Red, Console.ForegroundColor);

		Console.WriteLine(" Red");

		using (Color.Green)
		{
			Assert.AreEqual(ConsoleColor.Green, Console.ForegroundColor);

			Console.WriteLine("  Green");

			using (Color.Blue)
			{
				Assert.AreEqual(ConsoleColor.Blue, Console.ForegroundColor);

				Console.WriteLine("   Blue");
			}

			Assert.AreEqual(ConsoleColor.Green, Console.ForegroundColor);

			Console.WriteLine("  Green");
		}

		Assert.AreEqual(ConsoleColor.Red, Console.ForegroundColor);

		Console.WriteLine(" Red");
	}

	Assert.AreEqual(ConsoleColor.Gray, Console.ForegroundColor);

	Console.WriteLine("Gray");
}

With such a code, the intent is really clear.
What we obtain:

Console colors sample

Console colors sample

The console color perfectly matches the structure of our code with the nested using blocks.

Working with Other Use-Cases

Managing the console colors is a cool and useful use-case when you develop console applications, otherwise there is little chance you’ll need it in your daily developments.
In the second part of this article, I’ll show you some other use-cases for this pattern: the first is really general and consists in changing the current thread culture, the others are centered around Excel development.
Indeed, if you’ve ever worked with Excel automation, be it from VBA, VB.NET, C# or any other language, you’ve learnt the hard way that it can blow up at any time with some weird ComException and that you should be really careful with errors handling and state control, particularly when you change the “Application” object, otherwise you could completely mess up the whole Excel instance.

Scoping Change of Thread Culture

When you use an API whose output, e.g. stringification of dates and numbers, depends on the current culture to obtain predictable output, you need to set the current thread culture yourself, using the “CurrentCulture” property; and of course it is critical the original culture is used again after.
Here is an auto-reverting implementation:

public class Culture : IDisposable
{
	private readonly CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;

	public Culture(string cultureName)
		: this(CultureInfo.GetCultureInfo(cultureName))
	{
	}

	public Culture(CultureInfo culture)
	{
		Thread.CurrentThread.CurrentCulture = culture;
	}

	public void Dispose()
	{
		Thread.CurrentThread.CurrentCulture = this.originalCulture;
	}
}

You could use it like that:

using (new Culture("en-US"))
{
	// ...
}

using (new Culture(CultureInfo.InvariantCulture))
{
	// ...
}

Excel: Scoping Deactivation of Screen Updates

This may be the Excel global setting you tweak more often, because when your addin code is updating the sheets with new data, you don’t want Excel to display the changes on the fly because it could dramatically slow down updates.
Let’s build a component that will take care of changing and then reverting this setting:

public class ScreenUpdating : IDisposable
{
	private readonly bool originalValue;
	private readonly Application application = null;

	public ScreenUpdating(Application application, bool newValue)
	{
		this.originalValue = application.ScreenUpdating;
		this.application = application;

		this.application.ScreenUpdating = newValue;
	}

	public void Dispose()
	{
		this.application.ScreenUpdating = originalValue;
	}

	public static ScreenUpdating On(Application XL)
	{
		return new ScreenUpdating(XL, true);
	}

	public static ScreenUpdating Off(Application XL)
	{
		return new ScreenUpdating(XL, false);
	}
}

Note the two helper methods: “On” and “Off“.
When the set of possible states is limited, typically for boolean flags, you can enhance the readability of the code using this kind of helpers.

This is how we could use it:

using (ScreenUpdating.Off(XL))
{
    // update the sheets with new data
}

Excel: Scoping Activation of Manual Calculation

For similar reasons, you may want to disable automatic calculation and activate the manual calculation mode:

public class Calculation : IDisposable
{
	private readonly XlCalculation originalValue;
	private readonly Application application = null;

	public Calculation(Application application, XlCalculation newValue)
	{
		this.originalValue = application.Calculation;
		this.application = application;

		application.Calculation = newValue;
	}

	public void Dispose()
	{
		application.Calculation = originalValue;
	}

	public static Calculation Auto(Application XL)
	{
		return new Calculation(XL, XlCalculation.xlCalculationAutomatic);
	}

	public static Calculation Manual(Application XL)
	{
		return new Calculation(XL, XlCalculation.xlCalculationManual);
	}
}

And a usage sample:

using (Calculation.Manual(XL))
{
	// work without fearing recalculation
}

Excel: Scoping Sheet and Range Selection

Sometimes, you need to update a part of the workbook without losing the current user view, so preserving his current sheet or range selection:

public class SelectedSheet : IDisposable
{
	private readonly Worksheet originalSheet;
	private readonly Application application = null;

	public SelectedSheet(Application application, Worksheet sheet)
	{
		this.application = application;
		this.originalSheet = application.ActiveSheet as Worksheet;

		sheet.Select();
	}

	public void Dispose()
	{
		this.originalSheet.Select();
	}
}

public class Selection : IDisposable
{
	private readonly Worksheet originalSheet;
	private readonly Range originalSelection;
	private readonly Application application = null;

	public Selection(Application application, Range range)
	{
		this.application = application;
		this.originalSheet = application.ActiveSheet as Worksheet;
		this.originalSelection = application.Selection as Range;

		range.Worksheet.Activate();
		range.Select();
	}

	public void Dispose()
	{
		this.originalSheet.Activate();
		this.originalSelection.Select();
	}
}

Here is how you would use them:

using (new SelectedSheet(XL, sheet))
{
	// use the sheet
}

using (new Selection(XL, range))
{
	// use the range
}

Excel: Scoping Alerts Display

When you run some automated batches, you don’t want a stupid alert popup to stop it, waiting for the user to click the “OK” button, so you simply disable alerts during your processing, and you reactivate them after so that the user does not miss any important future warning:

public class Alerts : IDisposable
{
	private readonly bool originalValue;
	private readonly Application application = null;

	public Alerts(Application application, bool newValue)
	{
		this.originalValue = application.DisplayAlerts;
		this.application = application;

		this.application.DisplayAlerts = newValue;
	}

	public void Dispose()
	{
		this.application.DisplayAlerts = originalValue;
	}

	public static Alerts On(Application XL)
	{
		return new Alerts(XL, true);
	}

	public static Alerts Off(Application XL)
	{
		return new Alerts(XL, false);
	}
}

And a sample:

using (Alerts.Off(XL))
{
	// run some batch without interruption
}

Conclusion

You now have another pattern in your toolbox to enhance the quality of your code, making it more reusable and readable.

But be very careful not to abuse it as it is very tempting to apply it everywhere even when a simple try/finally block would be enough.
Indeed, the main goal of this pattern is reusability, not readability, so apply it only to recurring state control operations and avoid it if it only enhances readability because almost every C# programmer will understand your intent when you use the try/finally construct so using a disposable wrapper won’t add a lot of value and could even be confusing for some people.
If you have to ensure proper control of a state specific to an application, and only once, you really don’t need this pattern, simply use a plain old try/finally block.

On the contrary, the use-cases presented in this article, directly taken from real life coding, are good examples of legitimate uses because they each encapsulate a recurring issue you want to implement right once and use many times: changing console colors is needed in a lot of console applications, controlling the state of Excel is critical when you develop Excel addins, and changing the current thread culture will serve you in both type of application and in many others.

I’m sure you’ll find plenty of other use-cases for this pattern, so please share them with us by dropping a comment.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here