DEV Community

loading...

Generic Singletons

Andreas Jakof
I made my hobby into a profession. Love what i do and get money for it. In other words: "Life is good!"
・5 min read

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 is private and therefore non-public and it belongs to the instance. A static constructor or the static property Instance 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.

Discussion (13)

Collapse
thebuzzsaw profile image
Kelly Brown

Good.

Now never use singletons in your code. :D

Collapse
andreasjakof profile image
Andreas Jakof Author

Since I read the "Clean Code", I understand you better now.

Well, DI has it's own flaws (some errors only occure during runtime, which would have been compile time errors), but I am getting used to it and you could check for these errors in a separate unit test.

I still don't mind singletones, but you should not use static classes or objects you only get from a static class. Use Factories instead or DI, which handles the singleton problem for you.

I still like generics, though...

Collapse
thebuzzsaw profile image
Kelly Brown

Currently, the only true singletons I put up with are classes that are completely immutable with no configuration options or side effects. Even then, I'm hesitant to lock them down, but sometimes I am OK with hiding the constructor and exposing an Instance simply because creating new ones literally just wastes memory.

Collapse
devestacion profile image
DevEstacion

It's probably better you inject these instances instead of creating a static class to access a singleton objects.

Collapse
andreasjakof profile image
Andreas Jakof Author

Now I am curious... Why?

Collapse
thebuzzsaw profile image
Kelly Brown

I can write many paragraphs in response, but I want to start small. The short version is that singletons offer incredibly low value and bring a fair number of problems along for the ride. I actively avoid singletons in all my code now.

Thread Thread
andreasjakof profile image
Andreas Jakof Author

I usually do too, but in my case, creating a new instance takes a considerable amount of time. Having it instantiated once and then just using it, will have quite an impact on performance, considering about 120000 items, that are to be used with it in a go. Adding 1/2 second each time sums up to about 17 hours, just for creating new instances... not even considering memory fragmentation, which causes other issues.

So in my case, it is a tradeoff beween two evils, chosing the lesser one.
And again, there is no general right or wrong.

I know, that one can achieve almost the same result, with a slightly different design without Singletons, but then again, I know that, others won't necessarily.

And to be honest: having a good documentation is not always enough to keep others from using it wrong, so creating it in a way, other just cannot use it wrongly, is again the lesser evil.

Thread Thread
thebuzzsaw profile image
Kelly Brown • Edited

The situation you are describing does not call for a singleton. This is a mistake I made early on in my career. It's completely normal and healthy to have situations where you need to reuse one instance instead of creating new ones. A singleton is so much more than that, though. A singleton is a heavy-handed approach that introduces so many new restrictions on your application.

If you need one instance... then make and share one instance! That's it! Sharing one instance does not mean you need to make it global, statically initialized, and (henceforth) completely invisible as a dependency in method signatures. You don't need to choose evil at all! Just code pragmatically.

I can't remember where I read it, but I really liked what someone said: you should only use a singleton for a subsystem where there literally cannot be more than one instance and/or allowing the user to instantiate that class would lead to danger/instability. There are very few things in programming that fall under this category.

When design patterns are concerned, the most commonly used might be the factory and the singleton.

Goodness, I hope not. How many singletons are you seeing in your code bases? :(

Thread Thread
andreasjakof profile image
Andreas Jakof Author

The situation you are describing does not call for a singleton.

I know, but I really want to enforce the usage of just ONE instance of a type. And experience told me, that I need to enforce it, otherwise it won't be used like that.

If you need one instance... then make and share one instance! That's it! ... Just code pragmatically.

I'd prefer that approach, too, but then again... I saw, how others in the team used a class, I designed to load encrypted credentials... and that told me a lot about, what I need to enforce and how they are going to use it. It was as if the two pages extensive step-by-step examples were not there at all. And I made it as simple as var creds = MyCreds.Load(Certificate, encryptedData);

When design patterns are concerned, the most commonly used might be the factory and the singleton.

Goodness, I hope not. How many singletons are you seeing in your code bases? :(

Okay, maybe not the most commonly used, but the most well known.
Actually, there currently are 10 Singletons all of the same "kind", encapsulating access to an Azure AD Group each. I like to have a name for something instead of a GUID. User.HasService<Android>() is better readable than User.HasService(someGuid).

Actually, the Singletons I described in my initial blog post, weren't singletons at first, but became such only afterwards, when I saw, that even I not always followed my own intention of using just ONE instance of it. So I made it, that I had to and the compiler just won't let me do it otherwise. I like the compiler, he is my friend and if I am doing it right, he can tell me, whether I did something wrong or not.

Thread Thread
thebuzzsaw profile image
Kelly Brown • Edited

I totally understand the desire to steer developers into the "pit of success". This is just an area I feel is more appropriate for something like a Roslyn Analyzer or at the very least code reviews. (And again, your blog post was good. I just had to be first to share the dissenting voice.)

The common issues you'll hear come around testability. Having to 'reset' the singleton is non-trivial at times, and it basically means tests cannot run in parallel. Plus, I don't know about you, but I've had times where I later realized I do need more than one instance. This is where I feel it's important to always use dependency injection so that you are insulated from such decisions. When a method can freely grab SomeSingleton<SomeWidget>.Instance, that dependency is less visible. You'd have to know to "Find All References" on the singleton itself.

I love the discourse. :)

Thread Thread
andreasjakof profile image
Andreas Jakof Author

I like discussing such things as well. If you look at my post „Brilliant but also horribly wrong“, I had like to learn from others, who know more than me.

And I usually don‘t use Singletons much. But in this case, I think, it is fine. The Singletons are usable in multiple threads and they mostly let me compare the membership of a user in an AAD-Group. And they NEVER change. If they would, they would no longer be of the same kind, not only the same instance.

And I know about the issues with testability as well... this is where I have a considerable lack of experience, even though I am already in the business for about 10 years now.

That’s why I am currently looking into DI in .Net Core. To make my code more testable. But then again, the code is mostly glue between systems and I am struggling with the basic approach, hie I will test it. Currently there are some basic unit tests, but lots of untested code, because the CD/CI server does not have access to all the credentials for the different systems I am glueing together. 😳

Collapse
bradwellsb profile image
Bradley Wells • Edited

I don't like "never." What would you use in a client-side Blazor application for keeping up with app state if not a singleton service? Which then, by the way, still has to be injected.

Collapse
thebuzzsaw profile image
Kelly Brown • Edited

I'm actually super happy you brought this up. You're referring to services.AddSingleton<T>, yes? I argue that this method is misnamed. I think it should be renamed to AddShared or something like that. When you inject an object in this manner, it's merely instructing the framework to reuse a single instance. It doesn't suddenly bestow all the qualities of a singleton upon that class. I can still voluntarily create another instance somewhere. The constructor is not locked down per the singleton design pattern.

(Oh you said client-side Blazor. I've not made one of those. Regardless, you don't need a true singleton.)