When design patterns are concerned, the most commonly used might be the factory and the singleton.
As a quick disclaimer: This works with C#, but I don't know, if there are other languages out there (not .NET), which might support this.
I also try to make this as beginner friendly as I can, but even if generics are quite powerful, they can be unintuitive at some points. So if I missed something cruicial, don't hesitate to ask in the comments.
Whats a singleton?
In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one "single" instance. This is useful when exactly one object is needed to coordinate actions across the system. The term comes from the mathematical concept of a singleton. Wikipedia
Translated:
You have a type and there should be one and only one instance of that type.
Usually this is done, by making the constructor private and giving the class a static property... let's call it "Instance".
public class Singleton
{
//make the constructor private
private Singleton(){}
public static Singleton Instance { get; } = new Singleton();
}
//access it like this
var i = Singleton.Instance;
But what if you have multiple Singletons, that should behave the same?
You could still create the (now abstract) Singleton-BaseClass but with a protected constructor, have an abstract "GetInstance()" Method and override it in the derived classes?
Well there is one problem: Static Methods can be neither abstract nor virtual. So you need to create the boiler code over and over again.
In my case this were 10 (and growing) AAD-Groups, that all have the ability to check, if a user is a member, add a user and remove a user.
Generics to the rescue
Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters. This approach [...] permits writing common functions or types that differ only in the set of types on which they operate when used, thus reducing duplication ... Wikipedia
Translated: Generic programming allows you to write code, that can operate on specific types, which are not known yet, but will support this kind of code.
Pretty abstract? Yes it is, but I bet, most of you already used it at some point. List<T>
is one such generic type and it allows you to handle pretty much any type, make a list of it and use the "List-Methods" no matter, what type the list is made of.
The benefit over an untyped ArrayList is, that you know, what type you are working with as soon, as you create the List<T>
and only objects of that type T are allowed in the list. And even better, the compiler checks this for you.
Remember: The compiler is your friend, not your enemy. All it wants, is to protect you from doing mistakes.
But lets show you, how I did multiple Singletons with the same "base class" using generics and then go over the details.
public abstract class GenericSingleton<T> where T : GenericSingleton<T>
{
protected GenericSingleton(){}
public static T Instance { get; } = Create();
private static T Create()
{
Type t = typeof(T);
var flags = System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic;
var constructor = t.GetConstructor(flags, null, Type.EmptyTypes,
null);
var instance = constructor.Invoke(null);
return instance as T;
}
...
}
public class FirstSingleton : GenericSingleton<FirstSingleton>
{
private FirstSingleton(){} //make sure it cannot be instantiated by using new()
}
public class AnotherSingleton : GenericSingleton<AnotherSingleton>
{
private AnotherSingleton(){} //make sure it cannot be instantiated by using new()
}
//access it like this
FirstSingleton firstSiglenton = FirstSingleton.Instace;
AnotherSingleton anotherSingleton = AnotherSingleton.Instance;
The first thing to recognize are the pointy brackets. Already in the first line there is the GenericSingleton<T>
which means, that the class expects a generic type for instantiation much like the List<T>
.
Different from List<T>
, which works with all types, is the where T : GenericSingleton<T>
. I put a type constraint to T, and therefore restrict the types, that can be used as T to GenericSingleton<T>
. This might look weird at the beginning, but it basically restricts the types for T to derived classes.
And why the
<T>
again?
Very good question! When creating a generic type, the type is not yet "complete". When you call new List<int>()
you create a new type on the fly. In the same matter GenericSingleton<T>
is not yet fully complete and GenericSingleton<FirstSingleton>
is a different type than GenericSingleton<AnotherSingleton>
. Even though they are written in one class, they are different types.
This "on the fly type creation" is, what makes public static T Instance { get; } = Create();
possible in the first place.
Static properties are the same over all instances of the class. Which makes sense, because they are not part of any object (instance of the class) but of the type itself.
So, if the property would belong to GenericSingleton
, it would always have the same value. And if you created multiple singletons one after another, then (at the end) it would be an instance of the last type you created. BUT since it belongs to GenericSingleton<T>
and you have a new base class for each derived class just by creating a new derived class, it is unique for each derived class.
At last, there is the method private static T Create()
, which contains the "magic".... well it is not magic, but reflection. It circumvents the private constructors of the derived classes, which are there to make sure, the intended singletons cannot be instantiated with new().
Line by line:
- The
typeof(T)
is our derived singleton's class, which is known, since the class takes it as generic argument. - The
flags
describe a non-public (method or property) which belongs to the instance and not the type. The constructor in question isprivate
and therefore non-public and it belongs to the instance. A static constructor or the static propertyInstance
would have belonged to the type, but lets not get distracted. - We have the type of our class and the know the binding flags of the constructor we want to execute. So in the next line, we get the constructor from the type,
- which is invoked in the next line. But the Invoke(...) does return an object,
- so in the last line, we cast the object to the type of our Derived Class and return it.
I am hoping, I was able to bring you generics a bit closer. This is a pretty special case of using them with singletons but it also shows some of the underlaying behavior of generics in .NET as well.
Good.
Now never use singletons in your code. :D