Installing the supporting applications in the given directories is merely a recommendation: they're hardcoded in various spots in the source code.
Introduction
What's this article actually about?

- Brief intro to Generics
- Start building a
Generic
class : Kelvin
(the Generic
Serialization Helper)
- Generics 'with' keyword
- Testing using NUnit
- Checking test code coverage using
NCover
- Quick explanation of the
GenericBinder
nested in Kelvin
Background: Generics
I'm not going to do an 'introduction to Generics': there's plenty of introduction to Generics articles around via Google if you wish to read more.
However, it is interesting to know that although Generics have only recently appeared in .NET 2.0, the concept has been around for a long while - Microsoft Research published a research paper on Generics in May 2001, and released 'Gyro' extension for the Microsoft Shared Source CLI (MS-SSCLI) in May 2003. MSDN magazine printed their first Introducing Generics article in September 2003, and an updated article on the final release of .NET 2.0 in January 2006.
I suspect most developers will encounter Generics using the new System.Collections.Generic classes such as Collection
, Dictionary
, SortedDictionary
, List
, Queue
and Stack
. You might even find yourself implementing KeyedCollection
for a Business
Object with an 'embedded key', or extending any one of the above. It's possible you'll never need to write your own complete Generic
class.
But under what conditions "would" you write your own Generic
class from scratch? What else can you accomplish with Generic
s, besides Collection
s? What else, besides storage/sorting, is 'common' enough that it makes sense to genericize...?
Kelvin the Generic Serialization Helper
Firstly, an acknowledgement, because the idea for a Gene<code>
ric wrapper was not mine. Chris Webb deserves full credit for coming up with the most interesting proposal for a Generic
class that I've heard to-date (Thanks Chris!). His concept of a type-safe class to read/write to SQL Server 2005 XML columns was the genesis of this article.
A serialization wrapper is a good candidate for a Generic
implementation because it's the sort of operation that you might want to perform on pretty much any class, and have it type-checked. The initial test case was the serialization code in the Searcharoo3 project -- here's what the 'old' code looks like, and what we will replace it with:
OLD |
System.IO.Stream stream = new System.IO.FileStream(
Preferences.CatalogFileName+".dat", System.IO.FileMode.Create);
System.Runtime.Serialization.IFormatter formatter
= new System.Runtime.Serialization.Formatters.Binary.
BinaryFormatter();
formatter.Serialize(stream, this);
stream.Close();
|
PLANNED |
Kelvin<Catalog>.ToBinaryFile
(this,Preferences.CatalogFileName+".dat");
|
Since we already have some existing code, the best place to start the Kelvin
Generic
Serialization Helper is with that code. Firstly, we declare the class (notice it's a static
class - another new 2.0 feature) with a Generic
Type parameter (the <T>
bit). The first method ToBinaryFile
consists purely of the existing Searcharoo3 code, with the Generic
Typed object cryo
as the first parameter.
public static class Kelvin<T>
{
public static bool ToBinaryFile (T cryo, string fileName)
{
try
{
System.IO.Stream stream
= new System.IO.FileStream
(fileName, System.IO.FileMode.Create);
System.Runtime.Serialization.IFormatter formatter
= new System.Runtime.Serialization.Formatters.Binary.
BinaryFormatter();
formatter.Serialize(stream, cryo);
stream.Close();
return true;
}
catch (System.IO.DirectoryNotFoundException)
{
return false;
}
}
}
OT: what's with the name Kelvin
? Deserializing objects sometimes get referred to as 'dehydrating' them, as though the serialization process is similar to freeze-drying of food products, which are reconstituted/rehydrated by adding water. Kelvin (the temperature measure) begins at 'absolute zero', so it seemed appropriative. Excuse the geek humor - but what else would you call it, the GenericSerializationHelperClass()
?
Anyway, we now have the beginnings of a Generic
class. Since Searcharoo3 has a matching deserialize function, we'll include that too. This time the generic type T
is more important - notice how the return
values are cast to T
. Even though we don't know "what" the return type will be, we can use the Generic
type identifier T
from the class declaration public static class Kelvin<T>
anywhere a normal type would be used: as the method return type, to cast an object, as a method parameter type or a local variable.
public static T FromBinaryFile(string frozenObjectFileName)
{
if (System.IO.File.Exists(frozenObjectFileName))
{
System.IO.Stream stream = new System.IO.FileStream
(frozenObjectFileName, System.IO.FileMode.Open);
System.Runtime.Serialization.IFormatter formatter
= new System.Runtime.Serialization.Formatters.Binary.
BinaryFormatter();
try
{
return (T)formatter.Deserialize(stream);
}
catch (System.Runtime.Serialization.SerializationException)
{
stream.Position = 0;
formatter.Binder = new GenericBinder();
return (T)formatter.Deserialize(stream);
}
finally
{
stream.Close();
}
}
else
{
throw new System.IO.FileNotFoundException
(frozenObjectFileName+" was not found.");
}
}
P. S. Ignore the catch
block for now, it's discussed later.
With the Kelvin<T>.FromBinaryFile
and Kelvin<T>.ToBinaryFile
methods implemented, the most obvious place to test them was in Searcharoo3. The entire Save()
and Load()
methods were replaced by these two lines respectively (notice how the type parameter has been set to Catalog
, which is the class we need to serialize):
Kelvin<Catalog>.ToBinaryFile(this, Preferences.CatalogFileName + ".dat");
Catalog catalog = Kelvin<Catalog>.FromBinaryFile
(Preferences.CatalogFileName + ".dat");
Et voila, it works! That's a start, but to properly test our code, shouldn't we cover more than just one possible 'input'. And with Generic
classes, isn't the Type itself one of the inputs?
How to test Generic classes...
One of the challenges with Generic
code is going to be testing it! Just because Kelvin
works with one Type, doesn't mean it will work with ALL Types... for a start we know that System.Net.Mail.MailMessage
is a class that is specifically NOT Serializable. What would happen if we tried to use Kelvin.ToBinaryFile()
? A run-time Exception <code>
of course! So if we know that our Generic
class needs things to be Serializable
, how can we declare it so that Kelvin
will cause a compiler error rather than allow us developers to make that silly mistake? Using the where
syntax allows us to specify that our Generic
type MUST implement one or more interfaces, and hence tell the compiler exactly what types will work with our Generic
class!
public static class Kelvin<T> where T : ISerializable
Easy eh? Except in our case (i.e. for Kelvin
the Generic
Serialization
Helper), we probably DON'T want our type parameter to absolutely "require" ISerializable
implementation, because it would prevent us from serializing a whole pile of things, such as int[]
and string[]
. Seems strange, but for now we'll leave the where
clause off, although in other cases using where
would at least limit the number of types we needed to test!
How to test Generic classes with NUnit...
Back to the question, how can we test a Generic
class? I had hoped it might be possible to write a 'Generic
Test', like this:
[TestFixture]
public class KelvinFixture<T>
{
private T _objectToTest;
protected T ObjectUnderTest
{
set { _objectToTest = value; }
}
[Test]
public virtual void BinaryFileTest()
{
string filePath = GetFileSavePath(typeof(T).ToString() +
"BinaryFileTest.dat");
if (Kelvin<T>.ToBinaryFile(_objectToTest, filePath))
{
T t = Kelvin<T>.FromBinaryFile(filePath);
Assert.AreEqual(_objectToTest, t);
}
else
{
Assert.Fail("Could not save file to " + filePath);
}
}
}
then rely on NUnit's ability to recognise it's [Test*]
attributes via inheritance, then simply declare any number of concrete-type tests like this:
public class CatalogFixture : KelvinFixture<UnitTests.Catalog>
{
public CatalogFixture()
{
ObjectUnderTest = Catalog.GetTestInstance();
}
}
Unfortunately, this is not currently possible (NUnit 2.4 beta returns an error "UnitTests.KelvinFixture`1 : System.MemberAccessException : Cannot create an instance of UnitTests.KelvinFixture`1[T] because Type.ContainsGenericParameters is true."). Until there is a more 'elegant' solution (probably requiring explicit NUnit support for Generic
Tests), a slightly lower-tech approach is required - the Visual Studio solution in the download (64Kb) is shown below:
|
ConceptDevelopment project Kelvin.cs contains the class to be tested
|
UnitTests project References: ConceptDevelopment References: nunit.core , nunit.framework
- KelvinFixtureT.cs contains a pseudo-
Generic -TestFixture as described above
- <Type>Fixture.cs each contain a concrete implementation of KelvinFixture<T> for a specific type, where the ObjectUnderTest is set in the constructor
- Catalog.cs is a sample 'custom' Searcharoo3 class to test with
- SpecialCases.cs has special cases (such as verifying the
Exception thrown when T:MailMessage )
|
And because NUnit doesn't appear to handle Generic
types very neatly, our [TestFixture]
classes (which implement KelvinFixture<T>
) must also implement every pseudo-Test method, just to add the [Test]
attribute. The class diagram below shows test classes for arrays of ints and strings (Int32[]
, String[]
).

Note how each concrete class implements ALL the methods from KelvinFixture<T>
? Each of these overrides has exactly the same content, and rely on the constructor supplying an actual instance of the type to use in the tests. When adding new tests to the Generic
class, you must remember to add implementations in each concrete test (each concrete test is identical line-for-line except for the class declaration and constructor, so you may find yourself copy-pasting a lot as you add when new tests, or new types to test).
[Test]
public override
void BinaryFileTest()
{
base.BinaryFileTest();
}
Happily, both concrete instances pass our first test; and it was relatively easy to extend Kelvin
to serialize to/from a number of different formats (Byte[]
, String
, XmlDocument
, Binary File) and add new tests at the same time (click on the image to see all the passing tests).
There's something else fishy about these tests, although each [TestFixture]
consists of 11 [Test]
s, we're actually only testing ONE object (a 5-element Int32
Array and 6 element String
Array). Not exactly covering boundary cases! Thankfully once we have a concrete-typed Kelvin
class, any number of additional subclasses of the "concrete-typed" [Test]
can be created simply by overriding the constructor. The class diagram below shows additional tests with empty arrays and 32,000 element arrays. You might want to add more tests for subclasses of the T
type, with types that can be implicitly converted to T
, or any other cases you can think of!
Testing against custom classes
Now that we have a suite of tests that run against Int32[]
and String[]
, it's time to go back and write a proper TestFixture for Kelvin<Catalog>
. As with the other types, I implemented the Generic
class and supplied an instance of Catalog
in the constructor... but not all the tests passed!
If you look closely at the diagram below, you will be able to work out which tests failed (because I've removed them from the CatalogFixture
). Under normal circumstances, a Failed Test indicates a problem with your code, but in this case they merely exposed a known weakness in the Searcharoo.Net.Catalog
class: it doesn't support XML deserialization! If you're interested in "why", you should be able to figure it out just by looking at the internals of the Catalog
/Word
/File
classes and what they expose via properties... very difficult to reconstitute the object graph that way! Luckily binary serialization works in both directions (as our earlier tests showed), and for Searcharoo's production purpose that's all that is required.
Recall the discussion earlier about the where
clause, and why we didn't use it to restrict Kelvin
to ISerializable
types. Here's another example of why that can give you a false sense of security: Catalog
"does" implement ISerializable
, it just does a poor job which causes our otherwise 'correct' Kelvin
code to fail. When writing Generic
classes, always take care with the assumptions you make about the Types that could implement it... use where
if possible, but otherwise code defensively and try to give implementors as much assistance as possible to handle errors.

How to start NUnit from Visual Studio 2005
If you haven't used NUnit before, but have downloaded this project and want to give it a try, right-click on UnitTests-Properties to set the Start Action to NUnit, and the argument to the NUnit config file within the project.
|
|
How to check test coverage with NCover
You'll also notice the NUnit_NCover.cmd file in the project - NCover operates in conjunction with NUnit to report on which lines of code were executed while the tests were run. The CMD file is shown below (go to the NCover website for more info).
@echo off
C:\DevTools2\NCover-1.5\NCover.Console.exe
"C:\DevTools2\nunit-2.4.b1\bin\nunit-console.exe"
"C:\Inetpub\Kelvin\UnitTests\KelvinUnitTests.nunit"
//a "ConceptDevelopment"
//w "C:\DevTools2\nunit-2.4.b1\bin"
//l "C:\Inetpub\Kelvin\NCover.log.txt"
//x "C:\Inetpub\Kelvin\NCover.output.xml"
start C:\DevTools2\NCoverExplorer\NCoverExplorer.exe
C:\Inetpub\Kelvin\NCover.output.xml
Important: the above CMD file has had line-breaks added for readability. Note the directory locations of the programs and source files.
Used in conjunction with NCoverExplorer, you get output like that shown to the right, and the ability to view every line of code in your application, whether it was executed during the tests, and if so, how many times! Unfortunately NCover
occasionally exhibits unusual behaviour (or else I'm still not using it right!)... the sub-100% items in the KelvinUnitTests
should have higher coverage (by my calculations) as some of the lines it shows as 'unvisited' ARE definitely hit when I step through in the debugger. If I figure that out I'll update this para.
But what is GenericBinder for?
For history on the problem, once again visit Searcharoo3 and search down for "Loading the Catalog from Disk".
Basically, when you do Binary serialization, the "Type Information" is 'embedded' in the serialized stream so that when you come to DE-serialize it, the Framework can quickly and easily find the target type to instantiate and fill with data. The "Type Information" includes the source assembly, in the form App_Code.vj-e_8q4, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
; and you'll notice the "vj-e_8q4
" string appears to be a random jumble of characters... because it is! Between two AppDomain lifecycles, any class defined in an ASP.NET Web Application (code-inline in v1.x, or in App_Code in 2.0) is 'hardcoded' as an instance of a randomly-named assembly, so that if you try to DE-serialize after the Server Application has been restarted (or other event has caused recompilation), the type cannot be found and DE-serialization fails!. D'oh!
Thankfully, the Binary Deserialization code in the framework allows you to get around that by supplying a Custom Formatter - this is done by inheriting from SerializationBinder
and telling it which types to use. Searcharoo3 does just that, but you'll notice the types are hardcoded in the CatalogBinder
method... hardly a viable solution for a Generic
class!
The GenericBinder
class in Kelvin<T>
does these basic steps:
- the
BindToType
method gets parameters assemblyName
and typeName
assemblyName
is basically useless, as it's probably some randomly generated rubbish (recall that code using GenericBinder
is only called in a catch
block, so if the assemblyName
had been valid, we probably wouldn't be here!)
typeName
is fully qualified by namespace, such as System.Int32
or Searcharoo.Net.Catalog
- strip off the namespace from the
typeName
, so we're left with Catalog
. Note the assumptions behind this: firstly that the classname will be "unique" in the assembly; and secondly that the class "may" exist in a different namespace. In our example, we have binary data that was "serialized" from Searcharoo.Net.Catalog
but will be deserialized into UnitTests.Catalog
.
- try to load the assembly where Kelvin's
Generic
type is defined, using GetAssembly(typeof(T))
. This is a hack/guess, but the best place to start looking.
- try
a.GetType(assembly + "." + className);
to see if the 'unknown' type can be found, and if so use it
- if it works, we can use that type! if not, an
Exception
is thrown
The GenericBinder
will NOT work in all cases - but it does a pretty good job in the App_Code
situation. Notice that Kelvin<T>
provides an overload for FromBinary()
that allows consumers to provide their own Binder implementation as required:
public static T FromBinary(Byte[] frozen,
System.Runtime.Serialization.SerializationBinder customBinder)
There's probably a lot more you could do with the GenericBinder
class, including a wider search across all the assemblies you can find in the AppDomain
(with and without the fully qualified class namespace). I'll leave that as an exercise for the reader - and don't forget to write unit tests too!
Conclusion
That's quite a few different topics to cover in about 7 printed pages... hopefully it made some sense.
For further reading, you could try this Generic Range Class (and pattern) or these two CodeProject articles Generic Tree in C# and Generics Explained.
You might also find updated code on my website ConceptDevelopment.net, and in case you missed it, you might also be interested in reading about the free ASP.NET search engine: Searcharoo.
History
- 2006-07-10: posted on CodeProject