Optional Parameters in C# 4.0

As a C# developer who has done a fair amount of coding in VB.NET too, I've wanted to use optional parameters for some of my overloaded methods long before the capability existed.  Fortunately, optional parameters finally do exist in C# .NET 4.0.

Prior to .NET 4, something like the following needed to be done to effectively allow optional parameters by essentially overloading the same method name multiple times, with a different number of parameters in each signature:

class OptionalParametersTest
{
	public void SomeMethod(string a)
	{
		SomeMethod(a, "default value", "default value");
	}

	public void SomeMethod(string a, string b)
	{
		SomeMethod(a, b, "default value");
	}

	public void SomeMethod(string a, string b, string c)
	{
		Console.WriteLine(string.Format("a: {0}, b: {1}, c: {2}", a, b, c));
		// Additional code here
	}
}

Here's a sample invoking each of these three overloaded methods:

var optionalParametersTest = new OptionalParametersTest();

optionalParametersTest.SomeMethod("1");
optionalParametersTest.SomeMethod("1", "2");
optionalParametersTest.SomeMethod("1", "2", "3");

This will output:

a: 1, b: default value, c: default value
a: 1, b: 2, c: default value
a: 1, b: 2, c: 3

Here's the same class in the first listing, OptionalParametersTest, but rewritten using optional parameters:

class OptionalParametersTest
{
	public void SomeMethod(string a, string b = "default value", string c = "default value")
	{
		Console.WriteLine(string.Format("a: {0}, b: {1}, c: {2}", a, b, c));
		// Additional code here
	}
}

The output is exactly the same:

a: 1, b: default value, c: default value
a: 1, b: 2, c: default value
a: 1, b: 2, c: 3

The notation, adding " = default value" after the name of the parameter, is familiar to VB.NET developers too.  And, just like in VB.NET, optional parameters must be defined last; you can't make the first parameter optional if the next one isn't.

But what if you want the second parameter (optional) to remain optional but define the third (also optional) parameter?  How can you get the following result?

a: 1, b: default value, c: 3

The trick is to omit the second parameter and define the third with a colon:

optionalParametersTest.SomeMethod("1", c : "3");

In the above line, we didn't bother specifying the parameter named "b" and instead just indicated that we wanted to define parameter "c".  This instructed the method to use the default value of "b" while still allowing the "c" parameter to be passed in.

Man, there are so many times I wished I could have done this before C# 4.0 hit the scene.

Interprocess Communication With Memory-Mapped Files, Part 3

I wasn't intending to write a third piece on this, but after spending more time with the prototype from the previous post, I realized that we've got a little more redundant code than I typically like to see.  While it's still fresh, I'd like to take the time to clean up the code, making the whole thing more reusable and much easier to extend later if needed.

To recap one last time, we had written two separate applications:

  • ConsoleAppListener
    Responsible for performing work and shutting down gracefully when it receives a “shutdown” message, after it has finished processing a full unit of work.  Multiple instances can exist, but each has a different “unique name” and a separate ConsoleAppController responsible for starting and monitoring each instance.
  • ConsoleAppController
    Responsible for starting ConsoleAppListener, ensuring it stays running, and sending a “shutdown” message instructing the ConsoleAppListener to shutdown gracefully when it’s finished processing.  Multiple instances can exist, but each is responsible for monitoring a ConsoleAppListener with a different “unique name”.
In the previous post, both ConsoleAppListener and ConsoleAppController reused two crucial functions, which I've wrapped into reusable methods, BroadcastMessage and GetMessage:
private void BroadcastMessage(int message)
{
	_memoryMappedFile = MemoryMappedFile.CreateOrOpen(_memoryMappedFileName, MessageByteSize);

	using (var accessor = _memoryMappedFile.CreateViewAccessor(0, MessageByteSize))
	{
		accessor.Write(0, message);
	}
}

private int GetMessage()
{
	int messageReceived;

	using (var stream = _memoryMappedFile.CreateViewStream())
	{
		var reader = new BinaryReader(stream);
		messageReceived = reader.ReadInt32();
	}

	Console.WriteLine(string.Format("Broadcast '{0}' received", messageReceived));

	return messageReceived;
}

In addition, I found it was best to encapsulate this communication code into a single class that's common to both classes.  The recurring theme is to make each class do just one thing, and do it well.  For this reason I've created a new project, called "Shared", and wrapped all of the communication logic into one class called InterprocessCommunicator.  This class is referenced by both projects and contains both the BroadcastMessage() and GetMessage() methods.

I've also refactored the other two classes to offload their communication work to the new InterprocessCommunicator, resulting in less redundant and cleaner code.
Project ConsoleAppController\Program.cs:
// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

using System;
using System.Diagnostics;
using System.IO;
using Shared;

namespace ConsoleAppController
{
    /// <summary>
    /// Responsible for launching a ConsoleAppListener process with a specific instance name and monitor it,
    /// if one isn't already running. If a ConsoleAppListener is already running, it will begin monitoring.
    /// If the ConsoleAppListener shuts down, this will restart it.
    /// If the user presses any key, the ConsoleAppListener is instructed to shut down gracefully.
    /// </summary>
    class Program
    {
        private const string ClientAppInstanceName = "SampleApp";
        private const string ClientProcessFileName = @"C:\Code\Personal\InterprocessCommunication\ConsoleAppListener\bin\Debug\ConsoleAppListener.exe";

        private static bool _isClientProcessRunning;
        private static bool _isShuttingDown;

        private static readonly InterprocessCommunicator InterprocessCommunicator = new InterprocessCommunicator(ClientAppInstanceName);

        static void Main(string[] args)
        {
            // Determine whether the client process is already running:
            _isClientProcessRunning = InterprocessCommunicator.LocateClient();
            if (_isClientProcessRunning)
            {
                InterprocessCommunicator.ProcessExited += ProcessExited;
                Console.WriteLine("Process already running");
            }
            else
            {
                // Launch the process:
                LaunchProcess();
                Console.WriteLine("Process launched");
            }

            Console.WriteLine("Hit any key to send shutdown message (at any time)");
            Console.ReadKey();

            WantProcessToShutDown();
            Console.WriteLine("Sent message to shut down");

            _isShuttingDown = true;
            Console.WriteLine("Hit Enter to close");
            Console.ReadLine();
        }

        private static void LaunchProcess()
        {
            var process = new Process();

            // Create process to launch the desired application:
            var processProperties = new ProcessStartInfo
                                        {
                                            UseShellExecute = true,
                                            WorkingDirectory = Directory.GetParent(ClientProcessFileName).FullName,
                                            FileName = ClientProcessFileName,
                                            Arguments = ClientAppInstanceName,  // Pass in the application's unique name
                                            RedirectStandardOutput = false
                                        };

            Console.WriteLine(String.Format("Launching executable: {0} {1} ", processProperties.FileName,
                                      processProperties.Arguments));

            process.StartInfo = processProperties;
            process.EnableRaisingEvents = true;
            process.Exited += ProcessExited;
            process.Start();
            _isClientProcessRunning = true;
        }

        private static void ProcessExited(object sender, EventArgs e)
        {
            Console.WriteLine("Process exited");
            _isClientProcessRunning = false;

            // Start up the process again:
            if (!_isShuttingDown)
            {
                LaunchProcess();
            }
        }

        private static void WantProcessToShutDown()
        {
            if (!_isClientProcessRunning)
            {
                return;
            }

            InterprocessCommunicator.BroadcastShutdownMessage();
        }
    }
}

Project ConsoleAppListener\Program.cs:

// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

using System;
using System.Threading;
using Shared;

namespace ConsoleAppListener
{
    /// <summary>
    /// Responsible for performing a unit of work ("processing") at a time,
    /// checking for a message to shutdown between processing.
    /// </summary>
    class Program
    {
        private static string _instanceName;
        private static InterprocessCommunicator _interprocessCommunicator;

        static void Main(string[] args)
        {
            // Get the unique name of this application from the first commandline argument:
            _instanceName = args.Length > 0 ? args[0] : string.Empty;

            if (string.IsNullOrEmpty(_instanceName))
            {
                Console.WriteLine("Error: Expected a unique name for the application from the command line.");
                Console.Read();
                return;
            }

            _interprocessCommunicator = new InterprocessCommunicator(_instanceName);
            _interprocessCommunicator.BroadcastProcessId();

            while (!ShouldClose())
            {
                Console.WriteLine("Close message not received, beginning to process");

                // Perform a unit of work:
                PerformProcessing();

                Console.WriteLine("Processing finished");
            }

            Console.WriteLine("Close message received. Hit Enter to close");
            Console.Read();
        }

        private static bool ShouldClose()
        {
            return _interprocessCommunicator.CheckForShutdownMessage();
        }

        static void PerformProcessing()
        {
            Console.WriteLine("Performing processing");

            // Begin processing a unit of work

            // In this case, just sleep. In real world, processing code goes here:
            Thread.Sleep(6000);  // 6 sec
        }
    }
}
Project Shared\InterprocessCommunicator.vb:
// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;

namespace Shared
{
    public class InterprocessCommunicator
    {
        private const int MessageByteSize = 4;  // Number of bytes needed to store an int32
        private const int ShutdownMessage = -1;

        private MemoryMappedFile _memoryMappedFile;
        private readonly string _memoryMappedFileName;

        public EventHandler ProcessExited;

        public InterprocessCommunicator(string memoryMappedFileName)
        {
            _memoryMappedFileName = memoryMappedFileName;
        }

        #region Public Methods

        public bool LocateClient()
        {
            try
            {
                _memoryMappedFile = MemoryMappedFile.OpenExisting(_memoryMappedFileName);
            }
            catch (FileNotFoundException)  // Exception-driven logic is never ideal, but no method exists to check for the file first
            {
                return false;  // Client not found
            }

            // Read the process ID from the memory-mapped file:
            var messageReceived = GetMessage();

            // Check if the latest message in the file is a shutdown message
            // sent from a previous instance of this application
            // (which means that the client app is still running but no longer listening-- likely it's shutting down)
            if (messageReceived == ShutdownMessage)
            {
                return false;  // Client not found
            }

            var processId = messageReceived;

            // Get the client process by its ID:
            var process = Process.GetProcessById(processId);
            process.EnableRaisingEvents = true;
            process.Exited += ProcessExited;

            return true;  // Client found
        }

        public bool CheckForShutdownMessage()
        {
            var message = GetMessage();
            if (message == ShutdownMessage)
            {
                Console.WriteLine("Received shutdown message");
                return true;
            }

            return false;
        }

        public void BroadcastProcessId()
        {
            var processId = Process.GetCurrentProcess().Id;
            BroadcastMessage(processId);
        }

        public void BroadcastShutdownMessage()
        {
            BroadcastMessage(ShutdownMessage);
        }

        #endregion

        #region Private Methods

        private void BroadcastMessage(int message)
        {
            _memoryMappedFile = MemoryMappedFile.CreateOrOpen(_memoryMappedFileName, MessageByteSize);

            using (var accessor = _memoryMappedFile.CreateViewAccessor(0, MessageByteSize))
            {
                accessor.Write(0, message);
            }
        }

        private int GetMessage()
        {
            int messageReceived;

            using (var stream = _memoryMappedFile.CreateViewStream())
            {
                var reader = new BinaryReader(stream);
                messageReceived = reader.ReadInt32();
            }

            Console.WriteLine(string.Format("Broadcast '{0}' received", messageReceived));

            return messageReceived;
        }

        #endregion
    }
}
Previous articles:
Reference:

Interprocess Communication With Memory-Mapped Files, Part 2

In the previous post, I provided a glimpse into how to use a memory-mapped file to establish a primitive interprocess communication system, responsible for passing along the process ID from one process, and a "shutdown gracefully" message from another.

To recap, we have two separate applications:

  • ConsoleAppListener
    Responsible for performing work and shutting down gracefully when it receives a “shutdown” message, after it has finished processing a full unit of work.  Multiple instances can exist, but each has a different “unique name” and a separate ConsoleAppController responsible for starting and monitoring each instance.
  • ConsoleAppController
    Responsible for starting ConsoleAppListener, ensuring it stays running, and sending a “shutdown” message instructing the ConsoleAppListener to shutdown gracefully when it’s finished processing.  Multiple instances can exist, but each is responsible for monitoring a ConsoleAppListener with a different “unique name”.
It can be difficult to explain in words what code can sometimes help clear up, so below I am posting the Program.cs files for each of the two console applications.  Note that the path to the .exe of the Listener app is hard-coded as a constant in the Controller app.
ConsoleAppListener's Program.cs:

 

// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

namespace ConsoleAppListener
{
    /// <summary>
    /// Responsible for performing a unit of work ("processing") at a time,
    /// checking for a message to shutdown between processing.
    /// </summary>
    class Program
    {
        private const int FileSize = 4;  // Maximum number of bytes needed to hold an int32
        private const int CloseMessage = -1;

        private static string _instanceName;
        private static MemoryMappedFile _memoryMappedFile;

        static void Main(string[] args)
        {
            // Get the unique name of this application from the first commandline argument:
            _instanceName = args.Length > 0 ? args[0] : string.Empty;

            if (string.IsNullOrEmpty(_instanceName))
            {
                Console.WriteLine("Error: Expected a unique name for the application from the command line.");
                Console.Read();
                return;
            }

            _memoryMappedFile = MemoryMappedFile.CreateOrOpen(_instanceName, FileSize);

            using (var accessor = _memoryMappedFile.CreateViewAccessor(0, FileSize))
            {
                var processId = Process.GetCurrentProcess().Id;
                accessor.Write(0, processId);
            }

            while (!ShouldClose())
            {
                Console.WriteLine("Close message not received, beginning to process");

                // Perform a unit of work:
                PerformProcessing();
            }

            Console.WriteLine("Close message received. Hit Enter to close");
            Console.Read();
        }

        private static bool ShouldClose()
        {
            using (var stream = _memoryMappedFile.CreateViewStream())
            {
                var reader = new BinaryReader(stream);
                var message = reader.ReadInt32();
                if (message == CloseMessage)
                {
                    Console.WriteLine("Received shutdown message");
                    return true;
                }
            }

            return false;
        }

        static void PerformProcessing()
        {
            Console.WriteLine("Performing processing");

            // Begin processing a unit of work

            // In this case, just sleep. In real world, processing code goes here:
            Thread.Sleep(6000);  // 6 sec
        }
    }
}
ConsoleAppController's Program.cs:
// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;

namespace ConsoleAppController
{
    /// <summary>
    /// Responsible for launching a ConsoleAppListener process with a specific instance name and monitor it,
    /// if one isn't already running. If a ConsoleAppListener is already running, it will begin monitoring.
    /// If the ConsoleAppListener shuts down, this will restart it.
    /// If the user presses any key, the ConsoleAppListener is instructed to shut down gracefully.
    /// </summary>
    class Program
    {
        private const int MessageByteSize = 4;  // Number of bytes needed to store an int32
        private const int CloseMessage = -1;
        private const string ClientAppInstanceName = "SampleApp";
        private const string ClientProcessFileName = @"C:\Code\Personal\InterprocessCommunication\ConsoleAppListener\bin\Debug\ConsoleAppListener.exe";

        private static bool _isClientProcessRunning;
        private static bool _isShuttingDown;

        static void Main(string[] args)
        {
            // Determine whether the client process is already running:
            _isClientProcessRunning = LocateClient(ClientAppInstanceName);
            if (_isClientProcessRunning)
            {
                Console.WriteLine("Process already running");
            }
            else
            {
                // Launch the process:
                LaunchProcess();
                Console.WriteLine("Process launched");
            }

            Console.WriteLine("Hit any key to send shutdown message (at any time)");
            Console.ReadKey();

            WantProcessToShutDown(ClientAppInstanceName);

            _isShuttingDown = true;
            Console.WriteLine("Hit Enter to close");
            Console.ReadLine();
        }

        private static bool LocateClient(string clientAppUniqueName)
        {
            MemoryMappedFile memoryMappedFile;

            try
            {
                memoryMappedFile = MemoryMappedFile.OpenExisting(clientAppUniqueName);
            }
            catch (FileNotFoundException)  // Exception-driven logic is never ideal, but no method exists to check for the file first
            {
                return false;  // Client not found
            }

            int messageReceived;

            // Read the process ID from the memory-mapped file:
            using (memoryMappedFile)
            {
                using (var stream = memoryMappedFile.CreateViewStream())
                {
                    var reader = new BinaryReader(stream);
                    messageReceived = reader.ReadInt32();
                    Console.WriteLine(string.Format("Broadcast '{0}' received", messageReceived));
                }
            }

            // Check if the latest message in the file is a shutdown message
            // sent from a previous instance of this application
            // (which means that the client app is still running but no longer listening-- likely it's shutting down)
            if (messageReceived == CloseMessage)
            {
                return false;
            }

            var processId = messageReceived;

            // Get the client process by its ID:
            var process = Process.GetProcessById(processId);
            process.EnableRaisingEvents = true;
            process.Exited += ProcessExited;

            return true;
        }

        private static void LaunchProcess()
        {
            var process = new Process();

            // Create process to launch the desired application:
            var processProperties = new ProcessStartInfo
                                        {
                                            UseShellExecute = true,
                                            WorkingDirectory = Directory.GetParent(ClientProcessFileName).FullName,
                                            FileName = ClientProcessFileName,
                                            Arguments = ClientAppInstanceName,  // Pass in the application's unique name
                                            RedirectStandardOutput = false
                                        };

            Console.WriteLine(String.Format("Launching executable: {0} {1} ", processProperties.FileName,
                                      processProperties.Arguments));

            process.StartInfo = processProperties;
            process.EnableRaisingEvents = true;
            process.Exited += ProcessExited;
            process.Start();
            _isClientProcessRunning = true;
        }

        private static void ProcessExited(object sender, EventArgs e)
        {
            Console.WriteLine("Process exited");
            _isClientProcessRunning = false;

            // Start up the process again:
            if (!_isShuttingDown)
            {
                LaunchProcess();
            }
        }

        private static void WantProcessToShutDown(string destinationAppName)
        {
            if (!_isClientProcessRunning)
            {
                return;
            }

            var memoryMappedFile = MemoryMappedFile.OpenExisting(destinationAppName);

            using (var accessor = memoryMappedFile.CreateViewAccessor(0, MessageByteSize))
            {
                accessor.Write(0, CloseMessage);
            }

            Console.WriteLine("Sent message to shut down");
        }
    }
}

Ideally, this has provided you with the framework to create a basic method for allowing two processes to talk; the controller can notify the listener when it's time to shut down gracefully, and the listener can broadcast its process ID in case the controller shut down unexpectedly.

Note: Part 3 was written to refactor the above code in a more efficient and encapsulated format.  I'd highly recommend checking that out too.

Part 1
Part 3

Interprocess Communication With Memory-Mapped Files, Part 1

Memory-mapped files are great concept that have been buried in various versions of Windows for years.  Finally they've been exposed and incorporated into .NET 4.0 and are available in Visual Studio 2010.

A memory-mapped file is essentially just a file, given a unique name-- no extension necessary-- that exists only in memory.  It can be used to cache a large file into memory for faster access and manipulation.  In addition, since they're accessible from the system level, these files can be used to allow multiple processes to communicate (called "interprocess communication").  It's this method of communication that I'd like to try to implement here.

Suppose we have two separate processes, one to perform work, and the other that's simply responsible for starting the first-- like a Windows Service.  In this situation, we're assuming that the Windows Service can't simply perform the work in a child thread, but relies on that other process to do it.

I've created two C# projects, with one intended perform "processing" and the other to launch and communicate with the first:

  • ConsoleAppListener
    Responsible for performing work and shutting down gracefully when it receives a "shutdown" message, after it has finished processing a full unit of work.  Multiple instances can exist, but each has a different "unique name" and a separate ConsoleAppController responsible for starting and monitoring each instance.
  • ConsoleAppController
    Responsible for starting ConsoleAppListener, ensuring it stays running, and sending a "shutdown" message instructing the ConsoleAppListener to shutdown gracefully when it's finished processing.  Multiple instances can exist, but each is responsible for monitoring a ConsoleAppListener with a different "unique name".
Since a memory-mapped file has a unique name and persists only while a process is using it, it is a strong candidate for meeting these requirements.  The first process to start, expected to be the ConsoleAppController, is responsible for creating the memory-mapped file.  The second process, the ConsoleAppListener, will look for a memory-mapped file with the same unique name it was provided in its command-line arguments.
Here's a rough order of execution, although many of these things can happen in parallel:
  1. ConsoleAppController starts, checks if a memory-mapped file exists with the name "SampleApp"
  2. ConsoleAppController reads the memory-mapped file, checks if a process ID has already been recorded. If so, this means ConsoleAppListener is already running and will now be monitored.  Otherwise launches a new ConsoleAppListener process with the "SampleApp" passed as a command-line argument
  3. ConsoleAppListener starts, creates or opens memory-mapped file with the name "SampleApp"
  4. ConsoleAppListener saves its process ID to the memory-mapped file
  5. ConsoleAppListener performs processing, checking periodically for the shutdown message, "-1"
  6. ConsoleAppController monitors, restarts ConsoleAppListener if it shuts down on its own (e.g. user closes the window)
  7. ConsoleAppController saves a shutdown message, "-1", to the memory-mapped file, instructing ConsoleAppListener to shutdown at an appropriate time (when it's not performing processing)
So in this scenario, the mem0ry-mapped file only needs to be large enough to store an int32, representing either the ConsoleAppListener's process ID or a shutdown message of "-1".  The direction of either message is implied, as the listener only cares about receiving a "-1" and the controller is only interested in a process ID (which is greater than -1).  For that reason, I consider this mechanism more of a passive, broadcast-based communication system, rather than an active, full-duplex sort of client-server arrangement.
Here's some of the code used by the ConsoleAppListener, responsible for listening for a shutdown message:
private static bool ShouldClose()
        {
            using (var stream = _memoryMappedFile.CreateViewStream())
            {
                var reader = new BinaryReader(stream);
                var message = reader.ReadInt32();
                if (message == CloseMessage)
                {
                    Console.WriteLine("Received shutdown message");
                    return true;
                }
            }

            return false;
        }
Here's part of ConsoleAppController, responsible for locating the memory-mapped file ("destinationAppName" is the name of the memory-mapped file) and sending a shutdown message to the ConsoleAppListener if prompted by the user:
        private static void WantProcessToShutDown(string destinationAppName)
        {
            if (!_isClientProcessRunning)
            {
                return;
            }

            var memoryMappedFile = MemoryMappedFile.OpenExisting(destinationAppName);

            using (var accessor = memoryMappedFile.CreateViewAccessor(0, MessageByteSize))
            {
                accessor.Write(0, CloseMessage);
            }

            Console.WriteLine("Sent message to shut down");
        }
I hope this preview gives at least some insight into the use of memory-mapped files for primitive interprocess communication.  In tomorrow's post I'll be providing all of the sample code.

IDisposable Interface and Managed Resources

Very scarcely I'll see a developer disposing an object and I'll admit that I'm one of those developers who doesn't do this enough.  We're all aware of the garbage collector that runs at indeterminate times to clean up the mess we've left behind, so who cares whether we actually call an object's Dispose() method?  Why not just wait until the garbage collector does its thing?

Because the garbage collector won't free up unmanaged resources in your code.  These are files, handles, and streams.  These objects implement the IDisposable interface, which has only one method, Dispose().

In C#, there are two techniques for disposing of an object that implements the IDisposable interface:

  • In a try / finallyblock:
    var memoryStream = new MemoryStream();
    var bytes = new byte[128];
    try
    {
    	memoryStream.Read(bytes, 0, bytes.Length);
    }
    finally
    {
    	memoryStream.Dispose();
    } 
  • In a using statement:
    var memoryStream = new MemoryStream();
    var bytes = new byte[128];
    using (memoryStream)
    {
    	memoryStream.Read(bytes, 0, bytes.Length);
    } 

In the case of a using statement, the Dispose() method is implied at the end and is guaranteed to run even if an exception is hit; it's a fancy way of implementing the try / finally block.

In addition, if any of your classes use unmanaged resources, you'll want to implement IDisposable too.  Here's an example.  Please feel free to let me know if you see any issues with this implementation.

public class SpecializedMemoryStream : IDisposable
{
	private MemoryStream _memoryStream;
	private bool _isDisposed;

	/// <summary>
	/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
	/// </summary>
	public void Dispose()
	{
		Dispose(true);

		// Tell the garbage collector that the finalize destructor doesn't need to be called for this object
		GC.SuppressFinalize(this);
	}

	/// <summary>
	/// The finalize destructor
	/// </summary>
	~SpecializedMemoryStream()
	{
		Dispose(false);
	}

	protected virtual void Dispose(bool isDisposingManagedResources)
	{
		// Check if this method was previously called:
		if (_isDisposed)
		{
			return;
		}

		if (isDisposingManagedResources)
		{
			// Free up managed resource:
			if (_memoryStream != null)
			{
				_memoryStream.Dispose();
				_memoryStream = null;
			}
		}

		// Dispose any native resources here.

		_isDisposed = true;
	}
}

It's definitely good practice to be on the eye out for objects that implement the IDisposable interface; although it takes a bit more code to implement correctly, it frees up resources that otherwise wouldn't have been reclaimed until your application closes and might help to prevent memory leaks along the way.

References:
http://msdn.microsoft.com/en-us/library/system.idisposable.aspx
http://msdn.microsoft.com/en-US/library/ms244737(v=VS.80).aspx