I recently faced the challenge of creating the functionality that allows a class to be saved to disk. Normally I would use databases or other structured format like XML, but this time, the classes were invalid. Invalid in the sense of not having all its properties set and therefore wasn’t fit for saving to any structured data store. Default values to the properties of the class were not an option.

Serialization was the way to go. I knew that. XML serialization was not an option due to the fact that XML data must be structured, and this was not the case. The only way to go was to use binary serialization. A binary serialization is like taking a snapshot of a class/object at its current state and save it. It does not care about the structure of the class, it just takes a snapshot. Serialization is not a new thing, but with .NET it’s a lot easier.

Remember that it could be relatively expensive to serialize objects, so don’t use it without knowing the impact. I’ve made a C# 2.0 helper class that uses generics to deserialize the serialized data stream back to its original state. The example only works in C# 2.0.

#region Using

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Xml;
using System.Globalization;

#endregion

/// <summary>
/// A serialization helper class.
/// </summary>
public static class Serializer
{

private static object _Lock = new object();

#region Serialize

/// <summary>
/// Serialize an object and store it in a file.
/// </summary>
/// <param name="mode">The instance of an object to serialize</param>
/// <param name="instance">The instance of an object to serialize.</param>
/// <param name="filePath">The location on disk.</param>
/// <returns>A serialized MemoryStream.</returns>
public static void Serialize(SerializeMode mode, object instance, string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
MemoryStream stream = Serialize(mode, instance);
byte[] buffer = stream.ToArray();
fs.Write(buffer, 0, buffer.Length);
fs.Flush();
}
}

/// <summary>
/// Serializes any instances of any objects.
/// </summary>
/// <param name="mode">The instance of an object to serialize</param>
/// <param name="instance">The instance of an object to serialize.</param>
/// <returns>A serialized MemoryStream.</returns>
public static MemoryStream Serialize(SerializeMode mode, object instance)
{
if (instance == null)
throw new ArgumentNullException("The instance object is null.");

lock (_Lock)
{
MemoryStream stream = new MemoryStream();
switch (mode)
{
case SerializeMode.Xml:
SerializeXml(ref stream, ref instance);
return stream;

default:
SerializeBinary(ref stream, ref instance);
return stream;
}
}
}

private static void SerializeBinary(ref MemoryStream stream, ref object instance)
{
BinaryFormatter bfor = new BinaryFormatter();
bfor.Serialize(stream, instance);
bfor = null;
}

private static void SerializeXml(ref MemoryStream stream, ref object instance)
{
XmlSerializer ser = new XmlSerializer(instance.GetType());
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
xsn.Add(string.Empty, null);
ser.Serialize(stream, instance, xsn);
ser = null;
}

#endregion

#region Deserialize

/// <summary>
/// Deserialize an object from a byte array
/// </summary>
/// <param name="mode">The mode to deserialize the object.</param>
/// <param name="instance">Specify an instance of an object to deserialize.</param>
/// <param name="buffer">A byte array containing the serialized object, that needs
/// to be deserialized</param>
/// <returns>A deserialized object.</returns>
public static T Deserialize<T>(SerializeMode mode, T instance, byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException("The byte array is null.");

lock (_Lock)
{
switch (mode)
{
case SerializeMode.Xml:
return DeserializeXml<T>(instance, buffer);

default:
return DeserializeBinary<T>(instance, buffer);
}
}
}

/// <summary>
/// Deserialize an object from a location on disk or network.
/// </summary>
/// <param name="mode">The mode to deserialize the object.</param>
/// <param name="instance">Specify an instance of an object to deserialize.</param>
/// <param name="filePath">The path to the serialized object.</param>
/// <returns>A deserialized object.</returns>
public static T Deserialize<T>(SerializeMode mode, T instance, string filePath)
{
byte[] buffer = null;

try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
return Deserialize<T>(mode, instance, buffer);
}
}
finally
{
buffer = null;
}
}

/// <summary>
/// Deserializes an object from a byte array
/// </summary>
private static T DeserializeXml<T>(T instance, byte[] buffer)
{
using (MemoryStream stream = new MemoryStream(buffer))
{
XmlSerializer ser = new XmlSerializer(typeof(T));
instance = (T)ser.Deserialize(stream);
return instance;
}
}

/// <summary>
/// Deserializes the object from a byte array.
/// </summary>
/// <param name="instance">Specify an instance of an object to deserialize.</param>
/// <param name="array">A serialized byte array</param>
private static T DeserializeBinary<T>(T instance, byte[] array)
{
BinaryFormatter bf;
MemoryStream ms = null;
try
{
ms = new MemoryStream(array);
{
bf = new BinaryFormatter();
instance = (T)bf.Deserialize(ms);
return instance;
}
}
finally
{
bf = null;
if (ms != null) ms.Close();
ms = null;
}
}

#endregion

}

#region Enumeration

/// <summary>
/// The type of serialization.
/// </summary>
public enum SerializeMode
{
/// <summary>Will serialize to a binary format.</summary>
Binary,
/// <summary>Will serialize to an XML format.</summary>
Xml
}

#endregion

If you used to be a VB developer, but for some reason are writing C# now, you probably miss a couple of functions. I remember being a little frustrated when I did the conversion from VB.NET to C#. It was March 2005 and I made the shift on the .NET 2.0 beta platform. I couldn’t understand why C# didn’t have some of the simplest functions like IsNumeric() and IsDate(). Also Left(), Right() and Middle() were missing. You could add a reference to the Microsoft.VisualBasic namespace, but that seemed like overkill for a few simple methods.

So I decided to make my own static class in C# 2.0 called VBFunctions that would implement exactly the four methods I was missing so much. This seems pretty basic and it is. But I use these functions again and again and now I have a single place to find them. Here’s the class:

#region Using

using System;
using System.Globalization;
using System.Text.RegularExpressions;

#endregion

/// <summary>
/// A collection of commonly used functions
/// </summary>
public static class VBFunctions
{

/// <summary>
/// Checkes if a object is numeric or not. It wrappes the functionallity
/// of Double.TryParse method.
/// </summary>
public static bool IsNumeric(object expression)
{
if (expression == null)
return false;

double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number);
}

/// <summary>
/// Checkes if an object is of type DateTime. It wrappes the functionallity
/// of DateTime.TryParse method.
/// </summary>
public static bool IsDate(object expression)
{
if (expression == null)
return false;

if (expression.GetType() == typeof(DateTime))
return true;

DateTime date;
return DateTime.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out date);
}

/// <summary>
/// Returns the the left n charecters from a string. It wrappes the
/// functionallity of the Substring() method.
/// </summary>
/// <param name="text">The original string value</param>
/// <param name="length">The length of the text to return from the left</param>
public static string Left(string text, int length)
{
if (text == null)
throw new ArgumentNullException("The text cannot be null");

if (length >= text.Length)
return text;

return text.Substring(0, length);
}

/// <summary>
/// Returns the the right n characters of a string. It wrappes the
/// functionallity of the Substring() method.
/// </summary>
/// <param name="text">The original string value</param>
/// <param name="length">The length of the text to return from the right</param>
public static string Right(string text, int length)
{
if (text == null)
throw new ArgumentNullException("The text cannot be null");

if (length >= text.Length)
return text;

return text.Substring(text.Length - length);
}

/// <summary>
/// Returns the string sequence between firstCharacter and secondCharacter in a string.
/// </summary>
/// <param name="text">The original string</param>
/// <param name="firstCharacter">The first charecter in the sequence</param>
/// <param name="secondCharacter">The second charecter in the sequence</param>
public static string Middle(string text, string firstCharacter, string secondCharacter)
{
if (text == null)
throw new ArgumentNullException(string.Format("The text cannot be null");

if (!text.Contains(firstCharacter) || !text.Contains(secondCharacter))
return string.Empty;

int start = text.IndexOf(firstCharacter) + 1;
int stop = text.IndexOf(secondCharacter) - 2;
return text.Substring(start, stop);
}

}