Click here to Skip to main content
15,896,606 members
Articles / Programming Languages / C#
Article

Generic Types Don't Share Static Members

Rate me:
Please Sign up or sign in to vote.
3.74/5 (12 votes)
30 May 2008CPOL11 min read 52K   83   12   8
Avoid the pitfall of open vs. closed types. Closed generic types do not share statics!

Introduction

Using Generic types together with static members might not always work exactly as you would expect. In fact it works a little differently compared to the same non generic code! If you are coming from a non-generic coding background, this might feel strange at first. Even if you are an ‘advanced generics user’, I consider myself to be one, you might fool yourself into this devious trap. I recently did and when I later discovered the error I felt that it was worth writing it up as an article and sharing it. I have the problem space as well as one way to solve the problem outlined in this article together with runnable code.

What is the problem with generic types having statics? The problem is that generic types do not share the same static members between each other. The reason for this is whether a type is ‘open’ or ‘closed’. When you close a type, you assign a type to all of its generic parameters. It is easy to make a mistake here and think that something else applies.

Using the Code

I have attached to this article a set of C# code files that may be included in the Visual Studio project of your choice with the version of IDE that you currently use. The only requirement is that your code runs on .NET Framework 2.0 since this is the version that introduced Generics into C#. I have also used NUnit 2.4.

Problem Description

When you code a generic class and include a static member variable, you might have a piece of code that looks like this (all samples are over simplified for brevity):

C#
using System;
using System.Collections.Generic;

namespace Dotway.Singleton.GenericStatic
{
    public class GenericBadSingletonCollection where T : class, new() 
    {
        private static Dictionary<Type, object> instances =
        new Dictionary<Type, object>();

        public static T GetInstance()
        {
            object instance;

            if (!instances.TryGetValue(typeof(T), out instance))
            {
                instance = new T();
                instances.Add(typeof(T), instance);
            }
            return (T)instance;
        }
    
        public static int CountInstances { get { return instances.Count; } }
    }
}

Admittedly, the code sample here described has a bit of a giveaway in the naming of the class: There is something wrong here - something bad. Can you spot it? The code seems straight forward enough; it's the Singleton container classic code right? Well almost. In fact this code will work as intended with creating a Singleton instance of the type T and returning that same Singleton instance when asked for it a second time. Still there is something here which is wrong!

To make testing simple, I am using two trivial domain classes with no content at all throughout my code:

C#
namespace Dotway.Singleton.GenericStatic
{
     public class Square {}
     public class Triangle {}
}

All I am interested in is to instantiate them as Singletons and count the number of Singleton types instantiated.

To reveal the error, I have two Unit Tests which both pass even though the second one is designed to show the error! I opted for creating passing tests only since this is a tutorial and not the code you should copy and use in your project. So again to be clear; all tests are green but some reveal the error(s)!

C#
[Test]
public void GenericSingletonWithCollection_HasOneInstancePerGenericType()
{
    Square square = GenericBadSingletonCollection<Square>.GetInstance();
    Square square2 = GenericBadSingletonCollection<Square>.GetInstance();

    Assert.IsNotNull(square);
    Assert.IsNotNull(square2);
    Assert.AreSame(square, square2);
    
    Assert.AreEqual(1, GenericBadSingletonCollection<Square>.CountInstances);
}

[Test]
public void EachGenericSingletonInstances_HasItsOwnMemorySpace()
{
    
    GenericBadSingletonCollection<Square>.GetInstance();
    GenericBadSingletonCollection<Triangle>.GetInstance();
    
    Assert.AreEqual(1, GenericBadSingletonCollection<Square>.CountInstances);
    Assert.AreEqual(1, GenericBadSingletonCollection<Triangle>.CountInstances);
}    

The first test is fine and shows that if I call .GetInstance() twice to get a Square instance, it will indeed be the same instance returned twice. A Singleton.

The second test however reveals the error! I have declared a .CountInstances member which counts the number of Singletons in my Dictionary of Singleton instances. In both requests using the Square and Triangle type parameters, I get back the answer one (1) instance! But I have requested and created two instances; one Square and one Triangle! The number of Singleton instances should be two. In fact if I ask for another, unused, type, the answer will be zero.

This is the whole root of the problem: Each Generic class referenced by a type parameter – that means a ‘closed type’ – gets its own memory space in the runtime. It is not the same Dictionary<Type, object> being referenced by the GenericBadSingletonCollection<Square> and GenericBadSingletonCollection<Triangle> cases. There is one Dictionary instance for each type parameter which means that the Dictionary will be used to hold one instance only and never more than one instance. Thus a Dictionary would be a little superfluous, wouldn't you say? The following class and tests are actually an optimization of the first example (skipping the namespaces from now on).

C#
public class GenericSimpleSingletonCollection<T> where T : class, new()
{    
    private static T instance;
    
    public static T GetInstance()
        {
        if (instance == default(T))
        {
            instance = new T();
        }
        return instance;
    }

    public static int CountInstances { get { return instance == null ? 0 : 1; } }
}

We can use a single variable to hold the instance and always return 0 or 1 from the .CountInstances property. Not only is it an optimization due to the fact that we don't have to create a silly one-value-only Dictionary per type-parameter. For the keen eye, there is one more optimization. Since we know the type of the instance, it is no longer contained in a Dictionary of objects, we don't have to cast the instance back to (T) when we return from the GetInstance method.

And here are the same tests:

C#
[Test]
public void GenericSimpleSingleton_HasOneInstancePerGenericType()
{
    Square square = GenericSimpleSingletonCollection<Square>.GetInstance();
    Square square2 = GenericSimpleSingletonCollection<Square>.GetInstance();

    Assert.IsNotNull(square);
    Assert.IsNotNull(square2);
    
    Assert.AreSame(square, square2);
    
    Assert.AreEqual(1, GenericSimpleSingletonCollection<Square>.CountInstances);
}

[Test]
public void EachGenericSimpleSingletonInstances_HasItsOwnMemorySpace()
{
    
    GenericSimpleSingletonCollection<Square>.GetInstance();
    GenericSimpleSingletonCollection<Triangle>.GetInstance();

    Assert.AreEqual(1, GenericSimpleSingletonCollection<Square>.CountInstances);
    Assert.AreEqual(1, GenericSimpleSingletonCollection<Triangle>.CountInstances);
}

However the problem with this simplified solution remains the same. There is a bug in the code that our current tests are not catching. We cannot accurately count the number of Singleton instances created in our code (of all types) since we won't be able to know which types have been requested from our Generic+type combinations. Let's say it is a requirement to be able to count the number of Singleton instances in use. Agreed it is a strange requirement but for arguments sake. We have to introduce another solution here!

Before we do that, however, let’s return to the ”good ol´ days” of non-Generics and compare code:

C#
public class OldSchoolSingletonCollection
{
    private static Dictionary<Type, object> instances = new Dictionary<Type, object>();

    public static object GetInstance(Type type)
    {
        object instance;
        if (!instances.TryGetValue(type, out instance))
        {    
            instance = Activator.CreateInstance(type, null);
            instances.Add(type, instance);
        }
    return instance;
    }
    
    public static int CountInstances { get { return instances.Count; } }
} 

The ”OldSchool” class has no generics. Here are the tests:

C#
[Test]
public void OldSchoolSingletonCollection_HasOneInstancePerGenericType()
{
    Square square = (Square)OldSchoolSingletonCollection.GetInstance(typeof(Square));
    Square square2 = (Square)OldSchoolSingletonCollection.GetInstance(typeof(Square));

    Assert.IsNotNull(square);
    Assert.IsNotNull(square2);
    Assert.AreSame(square, square2);

    Assert.AreEqual(1, OldSchoolSingletonCollection.CountInstances);
}

[Test]
public void OldSchoolSingletonCollection_HasItsOwnMemorySpace()
{
    OldSchoolSingletonCollection.GetInstance(typeof(Square));
    OldSchoolSingletonCollection.GetInstance(typeof(Triangle));
    
    Assert.AreEqual(2, OldSchoolSingletonCollection.CountInstances);
}

But as you can see, this code runs exactly as required! The non-generic and generic versions of the same code do not behave alike. Herein lays the problem I am writing about in this article.

While the Generic class with a static member has one static memory location per Generic+type parameter combination, each ‘closed type’, (one for MyClass<Square> and one for MyClass<Triangle>) the exact same non-generic code will have one static memory location for all instances of MyClassNonGeneric. We have to treat each MyClass<T> where T is a unique type as if it was a separate type altogether. In fact it is from the perspective of the runtime. The advantage still is that the code definition of all types which are generic is shared across instances and across closed types. This is the main gain of using Generics. However I feel that this might not be the first thing one thinks about when trying to figure out Generics and so there is some value to writing it.

Let us, at this time, turn to the documentation on Generics and see what we find:

I've scanned the documentation located at MSDN > MSDN Library > Development Tools and Languages > Visual Studio 2008 > Visual Studio > Visual C# > C# Programming Guide > Generics (C# Programming Guide) for documentation on this behavior. I personally cannot find a section in any of the many subpages that explain this. All I can find is a passage at the bottom of the page ”Generics in the Run Time” that speaks about how a generic type is managed by the Run Time:

“The first time a generic type is constructed with any reference type, the runtime creates a specialized generic type with object references substituted for the parameters in the MSIL. Then, every time that a constructed type is instantiated with a reference type as its parameter, regardless of what type it is, the runtime reuses the previously created specialized version of the generic type. This is possible because all references are the same size.

For example, suppose you had two reference types, a Customer class and an Order class, and also suppose that you created a stack of Customer types:

C#
class Customer { } class Order { } Stack<Customer> customers;

At this point, the runtime generates a specialized version of the Stack<(Of <(T>)>) class that stores object references that will be filled in later instead of storing data. Suppose the next line of code creates a stack of another reference type, which is named Order:

C#
Stack<Order> orders = new Stack<Order>();

Unlike with value types, another specialized version of the Stack<(Of <(T>)>) class is not created for the Order type. Instead, an instance of the specialized version of the Stack<(Of <(T>)>) class is created and the orders variable is set to reference it. Suppose that you then encountered a line of code to create a stack of a Customer type:

C#
customers = new Stack<Customer>();

As with the previous use of the Stack<(Of <(T>)>) class created by using the Order type, another instance of the specialized Stack<(Of <(T>)>) class is created. The pointers that are contained therein are set to reference an area of memory the size of a Customer type. Because the number of reference types can vary wildly from program to program, the C# implementation of generics greatly reduces the amount of code by reducing to one the number of specialized classes created by the compiler for generic classes of reference types.

Moreover, when a generic C# class is instantiated by using a value type or reference type parameter, reflection can query it at runtime and both its actual type and its type parameter can be ascertained.”

This does not explain Open and Closed types. Perhaps it is somewhere else in the documentation? Please link in the comments if you like so we can all read it! Signature TaylorMichaelL explains this very well in the comments below so I thought I'd edit it in (Thanks Taylor!): “When the CLR runs across a closed type instantiation (or reference) it first checks to see if the closed type has been created. If it hasn't then it creates the final closed type, allocates memory for the static fields and then creates an instance (or invokes the member). An important point to remember about generic types is that all reference types share the same base implementation while value types each get their own. Therefore when the runtime looks for a closed type implementation it is looking for the general reftype version or a specific valuetype version. In the case of a reftype version it will find the implementation but it still needs to allocate the space for the static fields. In the case of valuetype versions it either finds an exact match (and hence nothing more to do) or it has to create the closed type (along with the static fields).”

So there you go! This is why it does not work as expected. Again I realize that this might be very much as you'd expect if you are used to these concepts! But if you are not, then this tutorial might help you see the difference!

Step by Step Refinement of the Situation

Time to begin solving the problem; or rather suggest ways to work around it!

Let’s start with making the methods generic but not the class. Naturally this works fine so just for completeness, I'll show you that code. This is the same as the non-generic situation, only we don't have to cast in our own code. The cast is done inside the generic methods. Something I like to call ‘fake generics’! Real generics do not cast at all.

C#
public class GenericSimpleSingletonCollection<T> where T : class, new()
{    
    private static T instance;

    public static T GetInstance()
    {
        if (instance == default(T))
        {
            instance = new T();
        }
        return instance;
    }

    public static int CountInstances { get { return instance == null ? 0 : 1; } }
}

And the tests:

C#
[Test]
public void SingletonContainerWithGenericMethods_HasOneInstancePerGenericType()
{
    Square square = GenericSimpleSingletonCollection<Square>.GetInstance();
    Square square2 = GenericSimpleSingletonCollection<Square>.GetInstance();

    Assert.IsNotNull(square);
    Assert.IsNotNull(square2);
    Assert.AreSame(square, square2);

    Assert.AreEqual(1, GenericSimpleSingletonCollection<Square>.CountInstances);
}

[Test]
public void SingletonContainerWithGenericMethods_SharesMemorySpace_AsAllways()
{
    
    SingletonContainerWithGenericMethods.GetInstance<Square>();
    SingletonContainerWithGenericMethods.GetInstance<Triangle>();

    Assert.AreEqual(2, SingletonContainerWithGenericMethods.CountInstances);
}

But what about a generic class; that was the real problem we were solving.

We need to move the state of the class so that it may be shared but we don't really care for anyone else to be able to access it. Intuitively one tries with a nested class:

C#
public class GenericSingletonContainerWithNestedStateClass<T> where T : class, new()
{
    public static T GetInstance()
    {
        return StateClass.GetInstance();
    }

    public static int CountInstances { get { return StateClass.CountInstances; } }

    private class StateClass
    {
        private static Dictionary<Type, object> instances = 
                new Dictionary<Type, object>();
    
        public static T GetInstance()    
        {    
            object instance;

            if (!instances.TryGetValue(typeof(T), out instance))
            {
                instance = new T();
                instances.Add(typeof(T), instance);
            }
            return (T)instance;
        }

        public static int CountInstances { get { return instances.Count; } }    
    }
}

Does this work? Let’s see the tests:

C#
[Test]
public void GenericSingletonContainerWithNestedStateClass_HasOneInstancePerGenericType()
{
    Square square = GenericSingletonContainerWithNestedStateClass<Square>.GetInstance();
    Square square2 = GenericSingletonContainerWithNestedStateClass<Square>.GetInstance();

    Assert.IsNotNull(square);
    Assert.IsNotNull(square2);
    Assert.AreSame(square, square2);

    Assert.AreEqual(1, GenericBadSingletonCollection<Square>.CountInstances);
}
[Test]
public void GenericSingletonContainerWithNestedStateClass_HasItsOwnMemorySpace()
{
    GenericSingletonContainerWithNestedStateClass<Square>.GetInstance();
    GenericSingletonContainerWithNestedStateClass<Triangle>.GetInstance();

    Assert.AreEqual(1,
        GenericSingletonContainerWithNestedStateClass<Square>.CountInstances);
    Assert.AreEqual(1,
       GenericSingletonContainerWithNestedStateClass<Triangle>.CountInstances);
}

No! It does not work. The behavior is the same!

We have to move the state of our container outside the generic class to be able to share it between generic+type instances:

C#
public class GenericSingletonContainerWithInternalStateClass<T> where T : class, new()
{
    
        public static T GetInstance()
    {
        return InteralStateClass.GetInstance<T>();
    }

    public static int CountInstances { get { return InteralStateClass.CountInstances; } }
    public static implicit operator T( 
        GenericSingletonContainerWithInternalStateClass<T> value)
    {
        return InteralStateClass.GetInstance<T>();
    }
}

internal class InteralStateClass
{
    private static Dictionary<Type, object> instances = new Dictionary<Type, object>();
    public static T GetInstance<T>() where T : class, new()
    {
        object instance;
        if (!instances.TryGetValue(typeof(T), out instance))
        {
            instance = new T();
            instances.Add(typeof(T), instance);
        }
        return (T)instance;
    }

    public static int CountInstances { get { return instances.Count; } }
}

Ok… THIS works I can reveal.

C#
[Test]
public void 
    GenericSingletonContainerWithInternalStateClass_HasOneInstancePerGenericType()
{
    Square square = 
        GenericSingletonContainerWithInternalStateClass<Square>.GetInstance();
    Square square2 = 
        GenericSingletonContainerWithInternalStateClass<Square>.GetInstance();

    Assert.IsNotNull(square);
    Assert.IsNotNull(square2);
    Assert.AreSame(square, square2);

    Assert.AreEqual(1, GenericSimpleSingletonCollection<Square>.CountInstances);
}

[Test]
public void GenericSingletonContainerWithInternalStateClass_SharesMemorySpace_AsAllways()
{
    GenericSingletonContainerWithInternalStateClass<Square>.GetInstance();
    GenericSingletonContainerWithInternalStateClass<Triangle>.GetInstance();

    Assert.AreEqual(2,
       GenericSingletonContainerWithInternalStateClass<Square>.CountInstances);
    Assert.AreEqual(2,
       GenericSingletonContainerWithInternalStateClass<Triangle>.CountInstances);
}

I don't know if you noted in my code above that I also added the use of the static implicit operator construct in the final class above? I kind of like it when I can find a place where it makes sense to use it. Here is one such place and here is a final test to show its usage:

C#
[Test]
public void 
    GenericSingletonContainerWithInternalStateClass_CanCorrectlyUseImplicitOperator()
{
    Square square = 
        GenericSingletonContainerWithInternalStateClass<Square>.GetInstance();
    
    GenericSingletonContainerWithInternalStateClass<Square> singletonGetterClass =
        new GenericSingletonContainerWithInternalStateClass<Square>();
    
    Square square2 = singletonGetterClass;

    Square square3 = new GenericSingletonContainerWithInternalStateClass<Square>();

    Assert.AreSame(square, square2);
    Assert.AreSame(square, square3);
    Assert.AreEqual(1,
        GenericSingletonContainerWithInternalStateClass<Square>.CountInstances);
    Assert.AreEqual(1,
        GenericSingletonContainerWithInternalStateClass<Triangle>.CountInstances);
} 

Pretty cool huh? The only thing I have against this approach is that I declare ‘new’ but might get the already created singleton. The ‘new’ I refer to is a new instance of the class GenericSingletonContainerWithInternalStateClass<T> and nothing else. Whatever the statement returns might be an already created singleton instance of T.

The Trivial Comparison

Is this really such a revelation? Well I chose to show you this situation from the "code sample with a flaw"-end. Here’s the trivial test which explains the same from the other direction:

C#
public class GenericTrivialStaticField<T>
{
    public static T Trivial { get; set; } 
    public static string AlsoTrivial { get; set; } 
}

[Test]
public void TrivialTest()
{
    GenericTrivialStaticField<string>.Trivial = "foo";
        
    var bar = GenericTrivialStaticField<int>.Trivial;
    
    Assert.AreNotEqual("foo", bar);
        
    string foo = GenericTrivialStaticField<string>.Trivial;

    Assert.AreEqual("foo", foo);
} 

[Test]
public void AlsoTrivialTest()
{
    GenericTrivialStaticField<string>.AlsoTrivial = "foo";
        
    var bar = GenericTrivialStaticField<int>.AlsoTrivial;
    
    Assert.AreNotEqual("foo", bar);
    
    string foo = GenericTrivialStaticField<string>.AlsoTrivial;
    
    Assert.AreEqual("foo", foo);
} 

Here it becomes quite clear that the memory locations of GenericTrivialStaticField<string> which is a Closed Generic Type and GenericTrivialStaticField<int> which is another Closed Generic Type cannot be the same!

Conclusion

I was looking for a solution where I did NOT want to inherit any behavior and still get a shared state over all my closed generic types. This does not work! If this is the code you need you have to locate the shared state outside of the generic type definition.

Even if you think you know how the language is constructed, you might still be surprised. Generic classes do not share memory location of static members! When a generic type is closed, it has to be considered a unique type in the runtime, same as a non-generic type. This means that you will not have the same memory location for static member variables between different closed types.

Points of Interest

Even if you are a samurai coder, you may get caught in mistakes like this one. The funny thing is that the code was developed using TDD and the behavior of the code was correct. Still the inner workings of the code underneath the surface were not what was expected! There was an actual bug in the code that misused memory in an unforeseen manner.

License

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


Written By
Web Developer
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
sthotakura2-Aug-13 3:02
sthotakura2-Aug-13 3:02 
GeneralAnother possible solution Pin
Jim Rogers27-Oct-08 15:40
Jim Rogers27-Oct-08 15:40 
Questioncould there be a more "generic" solution.... Pin
dELm26-Jun-08 11:14
dELm26-Jun-08 11:14 
QuestionAlternative Solution? Pin
Anthony.Liew2-Jun-08 16:35
professionalAnthony.Liew2-Jun-08 16:35 
QuestionWhat the big deal. Pin
Gevorg30-May-08 3:27
Gevorg30-May-08 3:27 
AnswerRe: What the big deal. Pin
KenMcC3-Jun-08 13:04
KenMcC3-Jun-08 13:04 
GeneralSpecification PinPopular
CoolDadTx30-May-08 3:27
CoolDadTx30-May-08 3:27 
GeneralRe: Specification Pin
KenMcC3-Jun-08 13:10
KenMcC3-Jun-08 13:10 

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.