我可以用这种方式为所有派生的单例定义一个抽象类吗?

本文关键字:定义 一个 抽象类 单例 方式 派生 我可以 | 更新日期: 2023-09-27 18:13:46

这是我的抽象类,每次我想创建一个单例时必须派生它:

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() { }
}

因此,每次我需要遵循单例设计模式时,我可以这样写:

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

这是完全正确的吗?如果不是,为什么?

编辑:

  • 在派生类示例中添加私有构造函数,并在抽象基类中调用。
编辑:

  • 重做类型参数初始化。

我可以用这种方式为所有派生的单例定义一个抽象类吗?

不,你不能,因为当你想使用new T()时,你应该有一个公共构造函数,它不同于单例定义。因为在单例中,你应该有一个私有的构造函数,在这种情况下(公共的),每个人都可以创建你的对象的新实例,它不是一个单例

自实现单例是一种反模式。如果您只实现一个工厂,则无需继承并将类锁定到特定的表单中:

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; } }
}

然而,您实际上是将工厂用作服务定位器,而服务定位器也被认为是一种反模式,因为您可以轻松地使用DI将Server实例注入到您的消费类中。

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

温莎风格注册:

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

注意到这里从来没有提到过单例这个词吗?你仍然会得到"对象的单个实例",但你不必编写代码来维护这种关系,而且你的类从一开始就不会受到"单例"概念的约束和破坏

您正在使用的这种方法有一个明显的缺点。将类与Singleton类耦合。除了通常不是solid之外,您还失去了从您可能需要的类派生的能力(如果该类不是Singleton)。

最佳实践是你需要使用依赖注入和IoC容器。所有的IoC容器都允许您指定类是否为Singleton。

这样,类就完全忽略了它被实例化为Singleton的事实,并且很容易在不改变依赖关系的情况下即时更改它。

c#不能保证何时创建静态字段_instance。这是因为c#标准简单地声明类(在IL中标记为BeforeFieldInit)可以在访问静态字段之前的任何时间初始化它们的静态字段。这意味着它们可以在第一次使用时初始化,也可以在之前的某个时间初始化,您无法确定何时初始化。

删除Lazy usage

我建议放弃Lazy结构并引入一个静态变量来创建实例。这样您就可以利用c#标准的BeforeFieldInit,但您将失去懒惰。虽然它在某种程度上是惰性的,但它不是在使用类型之前创建的。这很好,因为无论如何,大多数单例都是在引用时使用的。

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();
    }
}

公共部门问题

你现在的问题是new T()结构迫使你有一个公共部门。这对单身人士来说不是很好。您可以使用通过反射调用的私有变量来解决这个问题。

这只是对pjvds回复的评论(我不能以常规方式评论,因为我没有足够的分数…)

比起通过反射使用私有构造函数,你可以拥有一个私有的init方法,并在静态singleton方法中的"new T()"之后调用它。