The implementation of pseudo-static virtual methods I sketched in my previous post has one problem:![]()
the implementation is currently not truly static since a StaticClass object is instantiated every time it is referenced
To fix this behavior, we need to cache the instances of the pseudo-static classes. Which immediately raises another question: Do we instantiate a pseudo-static class as a singleton for all classes that declare the pseudo-static class, or do we instantiate on a per-class level.
More complexity ahead:
public enum StaticClassInstantiation
{
Singleton,
PerClass
}
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public class StaticClassInstantiationAttribute : Attribute
{
StaticClassInstantiation inst;
public StaticClassInstantiationAttribute(
StaticClassInstantiation instantiation)
{
this.inst = instantiation;
}
public StaticClassInstantiation Instantiation { get { return inst; } }
}
The StaticClassInstantiationAttribute defines the instantiation policy of a pseudo-static class. The StaticHelper helper class (introduced in the previous post) will interpret this attribute, so we need to extend the class to handle the new semantics:
public static class StaticHelper
{
static Dictionary<Type, object> singletons =
new Dictionary<Type, object>(); // index is static class
static Dictionary<Type, object> classes =
new Dictionary<Type, object>(); // index is class
These dictionaries keep track of instances of pseudo-class, created as singletons or per-class.
CreateInstance() retrieves or creates and stores these instances:
static Type GetStaticType(Type t)
{
var attrs = t.GetCustomAttributes(
typeof(StaticClassAttribute), true);
if (attrs.Length == 0)
return null;
return (attrs[0] as StaticClassAttribute).Type;
}
static object CreateInstance(Type objectclass, Type staticclass)
{
if (staticclass == null) return null; // or throw NotImplemented
var attrs = staticclass.GetCustomAttributes(
typeof(StaticClassInstantiationAttribute), true);
if (attrs.Length == 0 ||
(attrs[0] as StaticClassInstantiationAttribute).Instantiation
== StaticClassInstantiation.PerClass) // arbitrary default
{
if (classes.ContainsKey(objectclass))
return classes[objectclass];
var c = staticclass.GetConstructor(Type.EmptyTypes)
.Invoke(new object[] { });
classes.Add(objectclass, c);
return c;
}
else
{
if (singletons.ContainsKey(staticclass))
return singletons[staticclass];
var c = staticclass.GetConstructor(Type.EmptyTypes)
.Invoke(new object[] { });
singletons.Add(staticclass, c);
return c;
}
}
Get() methods are adapted to call CreateInstance():
public static object Get<T>()
{
var t = GetStaticType(typeof(T));
return CreateInstance(typeof(T), t);
}
public static S Get<T, S>() where S: class
{
return Get<T>() as S;
}
public static object Get(object o)
{
var t = GetStaticType(o.GetType());
return CreateInstance(o.GetType(), t);
}
public static S Get<S>(object o) where S: class
{
return Get(o) as S;
}
}
We extend the sample of the previous post by adding a property to the sample interface and its implementing classes, and defining the instantiation policies using the new StaticClassInstantiation attribute:
public interface IStaticSampleClass
{
void Hello();
string Data { get; set; }
}
[StaticClassInstantiation(StaticClassInstantiation.Singleton)]
public class StaticSampleClass : IStaticSampleClass
{
public void Hello()
{
Console.WriteLine("hello from " + this.GetType().Name);
}
public string Data { get; set; }
}
[StaticClassInstantiation(StaticClassInstantiation.PerClass)]
public class StaticSampleClass2 : IStaticSampleClass
{
public void Hello()
{
Console.WriteLine("bye from " + this.GetType().Name);
}
public string Data { get; set; }
}
[StaticClass(typeof(StaticSampleClass))]
public class SampleClass
{
}
public class SampleClassEx : SampleClass
{
}
[StaticClass(typeof(StaticSampleClass2))]
public class SampleClass2 : SampleClass
{
}
public class SampleClass3 : SampleClass2
{
}
To test the implementation, we set and get the pseudo-static property Data. StaticSampleClass is declared as singleton, and StaticSampleClass2 as per-class:
StaticHelper.Get<SampleClass, IStaticSampleClass>().Data =
"set SampleClass";
StaticHelper.Get<SampleClassEx, IStaticSampleClass>().Data =
"set SampleClassEx";
StaticHelper.Get<SampleClass2, IStaticSampleClass>().Data =
"set SampleClass2";
StaticHelper.Get<SampleClass3, IStaticSampleClass>().Data =
"set SampleClass3";
Console.WriteLine("get SampleClass: " +
StaticHelper.Get<SampleClass, IStaticSampleClass>().Data);
Console.WriteLine("get SampleClassEx: " +
StaticHelper.Get<SampleClassEx, IStaticSampleClass>().Data);
Console.WriteLine("get SampleClass2: " +
StaticHelper.Get<SampleClass2, IStaticSampleClass>().Data);
Console.WriteLine("get SampleClass3: " +
StaticHelper.Get<SampleClass3, IStaticSampleClass>().Data);
As expected, the resulting output is:
get SampleClass: set SampleClassEx get SampleClassEx: set SampleClassEx get SampleClass2: set SampleClass2 get SampleClass3: set SampleClass3
Final note: the instantiation of the pseudo-static classes in this example is not thread-safe. Thread-safety should be handled in the CreateInstance() method. A simple solution is to call the StaticHelper.Get() method in the static constructors of classes defining a [StaticClass] attribute.
