29 July 2010 - 11:50 PM / by Dominic Pettifer. 3 Comments for Static Variable Gotchas - Are Static Variables in Base Classes Shared In Derived Sub Classes?.
Development Gotchas - Something that caught me out recently is the behaviour of static variables in C# classes, and how they behave in derived classes. Also how does this differ with static variables in Generic<T> base classes? Finally, we use this understanding to create a generic base Singleton class.
First consider the following base class that declares a static int:
public abstract class Base
{
private static int MyInt = 0;
public int StaticValue
{
get { return MyInt; }
set { MyInt = value; }
}
}Then we define two sub types like so:
public class DerivedA : Base { }
public class DerviedB : Base { }Now we run this code:
DerivedA drvA = new DerivedA();
DerviedB drvB = new DerviedB();
for (int i = 0; i < 3; i++)
{
drvA.StaticValue++;
drvB.StaticValue += 5;
Console.WriteLine("DerivedA: " + drvA.StaticValue);
Console.WriteLine("DerivedB: " + drvB.StaticValue);
}Take a moment to think what the output is. You should get something like this:
DerivedA: 6 DerivedB: 6 DerivedA: 12 DerivedB: 12 DerivedA: 18 DerivedB: 18
The reason is because the static ‘MyInt’ variable declared in Base type is shared with all classes that derive from it. DerivedA and DerivedB won’t get their own copies of MyInt. So to answer the question: Yes, static variables are shared by derived clases. This is generally well understood in the .NET world, including by myself before I embarked on this article. It’s Generic<T> types that tripped me up.
Now consider the following generic base class:
public abstract class BaseGeneric<T>
{
private static int MyInt = 0;
public int StaticValue
{
get { return MyInt; }
set { MyInt = value; }
}
}Literally all we’ve done is replace ‘Base’ with ‘BaseGeneric<T>’ otherwise it’s the same base class as before. Now we derive 2 types from it:
public class DerivedGenericA : BaseGeneric<string> { }
public class DerivedGenericB : BaseGeneric<int> { }Note in one we fill in the T type as string, and int in the other. Now we run the following code:
DerivedGenericA drvA = new DerivedGenericA();
DerivedGenericB drvB = new DerivedGenericB();
for (int i = 0; i < 3; i++)
{
drvA.StaticValue++;
drvB.StaticValue += 5;
Console.WriteLine("DerivedGenericA: " + drvA.StaticValue);
Console.WriteLine("DerivedGenericB: " + drvB.StaticValue);
}Take a moment to think what the output will be. It will output the following:
DerivedGenericA: 1 DerivedGenericB: 5 DerivedGenericA: 2 DerivedGenericB: 10 DerivedGenericA: 3 DerivedGenericB: 15
This is strange. They don’t seem to be sharing the same static variable even though they derive from the same class, what is going on? How are generic types being treated differently?
A clue is in the class declaration itself:
public class DerivedGenericA : BaseGeneric<string> { }
public class DerivedGenericB : BaseGeneric<int> { }In one we’re filling out the T type as string and int in the other. This effectively creates two different base types. Basically BaseGeneric<string> is not the same type as BaseGeneric<int> and because they’re not the same type, they don’t share static variables. We can see this if we use the same T type in both derived classes:
public class DerivedGenericA : BaseGeneric<string> { }
public class DerivedGenericB : BaseGeneric<string> { }...and we run the same code above to print out the static value:
DerivedGenericA: 6 DerivedGenericB: 6 DerivedGenericA: 12 DerivedGenericB: 12 DerivedGenericA: 18 DerivedGenericB: 18
...because now they both derive from the same base type of BaseGeneric<string>
We can use this knowledge to our advantage if we want to create a Generic base class that forces each derived class to be a Singleton (a helper for easily creating Singleton classes).
public abstract class SingletonBase<T> where T : SingletonBase<T>
{
private static volatile T _instance = null;
private static volatile object _syncObject = new object();
public static T Instance()
{
if (_instance == null)
{
lock (_syncObject)
{
if (_instance == null)
{
Type teesType = typeof(T);
try
{
_instance = (T)teesType.InvokeMember(teesType.Name,
BindingFlags.CreateInstance | BindingFlags.Instance |
BindingFlags.NonPublic, null, null, null,
CultureInfo.InvariantCulture);
}
catch (MissingMethodException)
{
string message = teesType.FullName +
" must use either a private or protected " +
"constructor to be a Singleton.";
throw new TypeLoadException(message);
}
}
}
}
return _instance;
}
}We’re storing the single instance as a static property, but since the base is a generic type, this static variable won’t get shared amongst the derived types, so we can create multiple singleton classes. We’re using reflection to instantiate the derived class in order to get around the classes inaccessible protected/private constructor, we’re also enforcing that the derived class must use a protected or private default constructor in the first place (Note: Reflection code came from CodeBender, cheers!) An implementation is below:
public class ConfigData : SingletonBase<ConfigData>
{
protected ConfigData()
{
// Initialise singleton (guaranteed to only run once)
DatabaseConnection = WebConfigurationManager
.ConnectionStrings["DbConn"].ConnectionString;
}
public string DatabaseConnection { get; protected set; }
public int MaxRecords { get; set; }
}Voilà, instant singleton, we just extend SingletonBase with the generic type being the derived singleton itself, and use it like this:
ConfigData data1 = ConfigData.Instance();
ConfigData data2 = ConfigData.Instance();
data2.MaxRecords = 9999;
data1.MaxRecords = 10;
Console.WriteLine("MaxRecords: " + data2.MaxRecords);
Console.WriteLine("Both point to the same instance: " + (data1 == data2));Which will output:
MaxRecords: 10 Both point to the same instance: True
@Dilip I tried:
public class DerivedGenericA : BaseGeneric<string> { }
public class DerivedGenericB : BaseGeneric<Stream> { }...like you said but it still yeilded:
DerivedGenericA: 1 DerivedGenericB: 5 DerivedGenericA: 2 DerivedGenericB: 10 DerivedGenericA: 3 DerivedGenericB: 15
...I also tried two different value types. So reference vs. value types doesn't seem to make a difference.
Posted on 2 August 2010 - 5:41 PM / by Dominic Pettifer (Administrator)
I stand corrected then. I remember reading somewhere that reference types share a single generated code with only the types being substituted because its just a pointer to an opaque object. Value types on the other hand, can vary in their size and hence code needs to be generated every time you close a generic type with a value type.
That obviously doesn't apply to member/static variables -- just operations/methods.
Posted on 2 August 2010 - 8:53 PM / by Dilip
I think you will still end up sharing code as long as you keep using reference types. I didn't check this out but BaseGeneric<string> and BaseGeneric<Stream> should still let you share the same static variable defined in BaseGeneric<T>. Its only when you start using value types that each closed type will yield its own IL.
Posted on 2 August 2010 - 5:14 PM / by Dilip
Result of Stored Procedure using output params and a recordset (from the blog Output Parameters AND Recordsets From a Stored Procedure )
@andrewmy I'm thinking of using Git myself, what Windows client do you use? Is TortoiseGit the defacto standard?
about 5 hours ago from EchofonThe new Google Logo is awesome, almost as good as Pacman
about 7 hours ago from Echofon"Normalization is from the devil" - do you agree? http://ayende.com/Blog/archive/2010/09/06/normalization-is-from-the-devil.aspx
12:48 PM September 6th from Echofon@ellieemptylemon Thanks for those, although company I work for is looking to send us on training courses.
11:19 AM September 6th from Echofon