【问题标题】:Abstract base class to force each derived classes to be Singleton抽象基类强制每个派生类为单例
【发布时间】:2011-02-20 18:22:37
【问题描述】:

如何创建一个抽象类来强制每个派生类为 Singleton ?我使用 C#。

【问题讨论】:

  • 你需要抽象吗?您可以创建一个通用的 Singleton 类。虽然仍然可以实例化 T 类型的对象。
  • 是的。抽象基类应该强制子类是单例的。

标签: c# oop ooad


【解决方案1】:

这里是我的单例继承实现:

using System;
using System.Reflection;

namespace Mik.Singleton
{
    class Program
    {
        static void Main()
        {
            //You can not create an instance of class directly
            //Singleton1 singleton1 = new Singleton1();

            Singleton1 singleton1 = Singleton1.Instance;
            Singleton2 singleton2 = Singleton2.Instance;

            Console.WriteLine(singleton1.Singleton1Text);
            Console.WriteLine(singleton2.Singleton2Text);

            Console.ReadLine();
        }
    }

    public class SingletonBase<T> where T : class
    {
        #region Singleton implementation

        private static readonly object lockObj = new object();
        private static T _instance;

        protected SingletonBase() { }

        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (lockObj)
                    {
                        if (_instance == null)
                            _instance = CreateInstance();
                    }
                }
                return _instance;
            }
        }

        private static T CreateInstance()
        {
            ConstructorInfo constructor = typeof(T).GetConstructor(
                            BindingFlags.Instance | BindingFlags.NonPublic,
                            null, new Type[0],
                            new ParameterModifier[0]);

            if (constructor == null)
                throw new Exception(
                    $"Target type is missing private or protected no-args constructor: {typeof(T).FullName}");
            try
            {
                T instance = constructor.Invoke(new object[0]) as T;

                return instance;
            }
            catch (Exception e)
            {
                throw new Exception(
                    "Failed to create target: type=" + typeof(T).FullName, e);
            }
        }

        #endregion Singleton implementation
    }

    public class Singleton1 : SingletonBase<Singleton1>
    {
        private Singleton1() { }

        public string Singleton1Text { get; } = "Singleton1Text value";
    }

    public class Singleton2 : SingletonBase<Singleton2>
    {
        private Singleton2() { }

        public string Singleton2Text { get; } = "Singleton2Text value";
    }
}

【讨论】:

    【解决方案2】:

    我相信我试图实现类似的目标,即在一组类上强制执行一个通用接口和单例模式。这是我的解决方案:

    // Common interface of my singleton classes
    public interface IMySingletonClass
    {
        string ValueGetter();
    
        void ValueSetter(string value);
    }
    
    // Generic abstract base class
    public abstract class Singleton<T>: IMySingletonClass
    {
        private static readonly object instanceLock = new object();
        private static T instance; // Derived class instance
    
        // Protected constructor accessible from derived class
        protected Singleton()
        {
        }
    
        // Returns the singleton instance of the derived class
        public static T GetInstance()
        {
            lock (instanceLock)
            {
                if (instance == null)
                {
                    instance = (T)Activator.CreateInstance(typeof(T), true);
                }
                return instance;
            }
        }
    
        // IMySingletonClass interface methods
        public abstract string ValueGetter();
    
        public abstract void ValueSetter(string value);
    }
    
    // Actual singleton class
    public class MySingletonClass : Singleton<MySingletonClass>
    {
        private string myString;
    
        private MySingletonClass()
        {
            myString = "Initial";
        }
    
        public override string ValueGetter()
        {
            return myString;
        }
    
        public override void ValueSetter(string value)
        {
            myString = value;
        }
    }
    

    这是一个简单的测试:

    class Program
    {
        static void Main(string[] args)
        {
            MySingletonClass r1 = MySingletonClass.GetInstance();
            Console.WriteLine("R1 value = {0}", r1.ValueGetter());
            r1.ValueSetter("Changed through R1");
            MySingletonClass r2 = MySingletonClass.GetInstance();
            Console.WriteLine("R2 value = {0}", r2.ValueGetter());
    
            Console.ReadKey();
        }
    }
    

    请注意,如果您只需要基本的“模板”,您可以轻松地从通用抽象单例类中删除通用接口。

    【讨论】:

      【解决方案3】:

      这是一种(丑陋的)方法。它可能会被简化和改进,但这是我第一次尝试。

      想法是首先使基类成为泛型抽象类(如上面的 cmets 所述),但类型参数被限制为从基类本身派生。这允许基类处理派生类型的单例实例。请注意,所有派生类都应该被密封,就像任何单例类一样。

      接下来,允许使用受保护的构造函数,但需要接受一个特殊类的实例,SingletonKey,它是一个修改后的单例。派生类可以访问 SingletonKey 类定义,但基类保留对唯一允许的实例的私有控制,从而对所有派生对象的构造进行控制。

      第三,基类需要能够调用派生类的构造函数,但这有点棘手。如果您尝试调用派生的键控构造函数,编译器会报错,因为它不能保证存在。解决方案是添加派生类必须初始化的静态委托。所以任何派生类都需要提供一个简单的初始化方法。在代码中首次尝试访问实例之前,应显式调用此初始化方法,否则将导致运行时错误。

      public abstract class Singleton<T> where T : Singleton<T>
      {
          protected Singleton(SingletonKey key) { }
          private static SingletonKey _key;
          private static SingletonKey Key
          {
              get
              {
                  if (_key == null) SingletonKey.Initialize();
                  return _key;
              }
          }
          protected class SingletonKey
          {
              private SingletonKey()
              {
              }
              public static void Initialize()
              {
                  if (_key == null)
                  {
                      _key = new SingletonKey();
                  }
              }
          }
      
          protected static Func<SingletonKey, T> Creator;
          private static T instance;
          public static T Instance
          {
              get
              {
                  if (instance == null) instance = Creator(Key);
                  return instance;
              }
          }
      }
      
      public class MySingleton : Singleton<MySingleton>
      {
          public string Name { get; set; }
          public static void Initialize()
          {
              Creator = (key) => new MySingleton(key);
          }
          protected MySingleton(SingletonKey key) : base(key)
          {
          }
      }
      

      【讨论】:

        【解决方案4】:

        java 或 C# 中的类不是“一流的”。类的静态部分不能被子类继承或覆盖。有关详细信息,请参阅this answer。另外,您没有元类的概念。

        在 Smalltalk 或 Ruby 等语言中,您可以定义一个新的元类 Singleton,它定义了一个方法 getInstance。然后您可以将ClassAClassB 定义为Singleton 元类的实例。然后两个类都会自动公开一个方法getInstance,该方法可用于创建实例objectAobjectB。这不是很酷吗?好吧,实际上您并不经常使用元类,而单例实际上是它们唯一有意义的用法,而且我知道。

        【讨论】:

        • 为提及 Smalltalk 点赞
        【解决方案5】:

        单例意味着拥有私有构造函数。但是你知道私有成员不能被继承。在 C++ 中有模板,因此您可以从模板类创建单例。在 C# 中,没有模板,因此您必须为所需的每个单例编写自己的私有构造函数。

        【讨论】:

          【解决方案6】:

          当您想要进行编译时检查时,这是不可能的。通过运行时检查,您可以做到这一点。这不漂亮,但这是可能的。这是一个例子:

          public abstract class Singleton
          {
              private static readonly object locker = new object();
              private static HashSet<object> registeredTypes = new HashSet<object>();
          
              protected Singleton()
              {
                  lock (locker)
                  {
                      if (registeredTypes.Contains(this.GetType()))
                      {
                          throw new InvalidOperationException(
                              "Only one instance can ever  be registered.");
                      }
                      registeredTypes.Add(this.GetType());
                  }
              }
          }
          
          public class Repository : Singleton
          {
              public static readonly Repository Instance = new Repository();
          
              private Repository()
              {
              }
          }
          

          【讨论】:

          • 那不是单身。使用单例模式,您对getInstance 的调用将始终成功。您的提议确实只是一个运行时检查,它没有提供单例模式的任何好处。
          【解决方案7】:

          这是行不通的,因为单例需要在某个地方进行静态访问,并且不能强制。

          对于单例实现 + 示例,请参阅:Implementing the Singleton Pattern in C#

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-19
          • 2015-03-23
          • 1970-01-01
          • 2017-06-21
          • 1970-01-01
          • 2021-07-24
          • 2015-09-14
          • 1970-01-01
          相关资源
          最近更新 更多