Can I define an abstract class for all derived Singletons in this way?

三世轮回 提交于 2020-01-14 07:06:07

问题


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.

回答1:


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




回答2:


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.




回答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.




回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/8036883/can-i-define-an-abstract-class-for-all-derived-singletons-in-this-way

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!