12

This is my abstract class which must be derived each time I want to make a Singleton:

public abstract class Singleton<T> where T : Singleton<T>
{
    private static readonly Lazy<T> _instance = new Lazy<T>(() =>
    {
        var constructor = typeof(T).GetConstructor(BindingFlags.NonPublic |
            BindingFlags.Instance, null, new Type[0], null);

        return (T)constructor.Invoke(null);
    });
    public static T Instance { get { return _instance.Value; } }
    public Singleton() { }
}

So, every time I need to follow the Singleton design pattern, I can just make this:

sealed class Server : Singleton<Server>
{
    private Server() { }
    ...
}

Is this completely right, and, if not, why?

Edit:

  • Added private constructor on derived class example and invoking on abstract base.

Edit:

  • Reworked type parameter initialization.
AgentFire
  • 8,944
  • 8
  • 43
  • 90
  • 9
    Shouldn't your constructor be private or protected? – vc 74 Nov 07 '11 at 13:10
  • @vc74 Not in this case. Try it and you will see the that you cannot derive without creating a constructor. – AgentFire Nov 07 '11 at 13:13
  • Right, but the singleton pattern doesn't allow for direct access to the constructor, no? Not that you can create an instance of an abstract class either... hmmm – hunter Nov 07 '11 at 13:14
  • 2
    If the constructor of the Singleton class is private you will get a compilation error saying: 'Singleton.Singleton()' is inaccessible due to its protection level. You need to make the constructor of the Singleton class protected. – Diego Nov 07 '11 at 13:15
  • @vc 74 has rightly pointed out the most obvious flaw in your code. Your generic singleton will not work without the generic constraint 'new()' but requiring Server to have a public parameterless constructor means that client code can instantiate Server directly, instead of using the singleton's Instance. – Paolo Falabella Nov 07 '11 at 13:17
  • 1
    The static readonly `_instance` field will be shared across all instances of `Singleton`. It doesn't matter how many objects you create of the generic type, you'll have a single `Instance` object. The terminology is a little off, this is more of a Flyweight pattern than a Singleton pattern. – Joseph Yaduvanshi Nov 07 '11 at 13:19
  • possible duplicate of [Generic Singleton](http://stackoverflow.com/questions/2319075/generic-singletont) – Paolo Falabella Nov 07 '11 at 13:22
  • 1
    Your edit made a lot of changes to your question, my answer was valid for your first question, in all it was better to ask a new question instead of completely changing face of original question. – Saeed Amiri Aug 16 '12 at 22:59

5 Answers5

8

No you can't because when you want to use new T() you should have a public constructor for it, and it's different from singleton definition. because in singleton you should have a private constructor, and in this case (public one) everyone can create new instance of your object and it's not a singleton.

Saeed Amiri
  • 22,252
  • 5
  • 45
  • 83
3

This approach you are using has a significant downside. You couple your classes to the Singleton class. Apart from being generally not-SOLID, you lose the ability to derive from a class you might need to (if the class is not Singleton).

The best practice is that you need to use Dependency Injection and IoC container. All IoC containers allow you to specify weather the class is a Singleton or not.

This way the class is completely oblivious to the fact that it is instantiated as a Singleton, and it is easy to change this on-the-fly without changing dependencies.

Boris Yankov
  • 1,530
  • 14
  • 21
3

Self implemented singletons are an anti-pattern. The need to inherit, and lock your classes into a specific form goes away if you just implement a factory:

public class Server {} //Not coupled to any inheritance hierarchy.

public class Factory
{
    private readonly Lazy<Server> _server = new Lazy<Server>(() => new Server());

    public Server Server { get { return _server.Value; } }
}

However, you're really using the factory as a service locator and service locator is also considered an anti-pattern as you can easily just use DI to inject the Server instance into your consuming classes.

public class MyServerConsumer
{
    public MyServerConsumer(Server server)
    {
      //Do stuff.      
    }
}

Windsor style registration:

 ... 
 Component.For<Server>();
 ...

Notice that the word singleton is never mentioned? You still get 'a single instance of an object', but you don't have to write code to maintain that relationship, and your classes are not constrained and corrupted by the concept of 'singleton' from the start

Ritch Melton
  • 11,498
  • 4
  • 41
  • 54
1

C# gives you no guarantee of when the static field _instance will be created. This is because the C# standard simply states that classes (which are marked in the IL as BeforeFieldInit) can have their static fields initialized any time before the field is accessed. This means that they may be initialized on first use, they may be initialized at some other time before, you can't be sure when.

Drop Lazy usage

I suggest to drop the Lazy construction and introduce a static ctor to create the instance. This way you take advantage of the C# standard's BeforeFieldInit, but you will lose lazyness. Although it is lazy in some way, it's not created before the type is used. Which is good, since most singletons are used when referenced anyway.

public abstract class Singleton<T> where T : class, new()
{
    private static readonly T _instance;
    public static T Instance { get { return _instance; } }
    public static Singleton() 
    {
        _instance = new T();
    }
}

Public ctor problem

The problem you have now is that the new T() construction forces you to have a public ctor. Which isn't really nice for singletons. You could use a private ctor that you invoke via reflection to solve this.

pjvds
  • 938
  • 6
  • 11
0

That's just a comment on pjvds' reply (I couldn't comment in the regular way because I don't have enough points...).

Instead of using a private constructor via reflection, you can have a private init method, and call it after the "new T()" in the static Singlton method.

Ofer
  • 423
  • 6
  • 11