【问题标题】:Can I define an abstract class for all derived Singletons in this way?我可以用这种方式为所有派生的 Singleton 定义一个抽象类吗?
【发布时间】:2026-02-20 04:35:01
【问题描述】:

这是我每次要创建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() { }
}

所以,每次我需要遵循单例设计模式时,我都可以这样做:

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

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

编辑:

  • 在派生类示例中添加了私有构造函数并在抽象基础上调用。

编辑:

  • 重做类型参数初始化。

【问题讨论】:

  • 你的构造函数不应该是私有的还是受保护的?
  • @vc74 在这种情况下不是。试一试,你会发现不创建构造函数就无法派生。
  • 如果 Singleton 类的构造函数是私有的,您将收到编译错误消息:“Singleton.Singleton()”由于其保护级别而无法访问。您需要使 Singleton 类的构造函数受到保护。
  • 静态只读_instance 字段将在Singleton&lt;T&gt; 的所有实例之间共享。不管你创建多少泛型类型的对象,你都会有一个Instance 对象。术语有点偏离,这更像是享元模式而不是单例模式。
  • 您的编辑对您的问题进行了很多更改,我的回答对您的第一个问题有效,总而言之,最好提出一个新问题,而不是完全改变原始问题的面貌。

标签: c# .net c#-4.0 singleton abstract-class


【解决方案1】:

这只是对pjvds回复的评论(我无法以常规方式发表评论,因为我没有足够的积分......)。

您可以拥有一个私有的init方法,而不是通过反射使用私有构造函数,并在静态Singlton方法中的“new T()”之后调用它。

【讨论】:

    【解决方案2】:

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

    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 将服务器实例注入到您的消费类中。

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

    温莎风格注册:

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

    注意到单例这个词从未被提及?您仍然可以获得“对象的单个实例”,但您不必编写代码来维护这种关系,并且您的类从一开始就不受“单例”概念的约束和破坏

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        C# 不保证何时创建静态字段 _instance。这是因为 C# 标准仅声明类(在 IL 中标记为 BeforeFieldInit)可以在访问字段之前的任何时间初始化其静态字段。这意味着它们可能在第一次使用时被初始化,它们可能在之前的某个时间被初始化,你不能确定什么时候。

        删除惰性使用

        我建议放弃Lazy 构造并引入静态ctor 来创建实例。这样你就可以利用 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() 构造迫使您拥有一个公共 ctor。这对单身人士来说并不是很好。您可以使用通过反射调用的私有 ctor 来解决此问题。

        【讨论】:

        • 一切都不如private static readonly T _instance = new T();
        【解决方案5】:

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

        【讨论】:

          最近更新 更多