抽象基类强制每个派生类为Singleton

时间:2010-05-18 07:31:00

标签: c# oop ooad

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

7 个答案:

答案 0 :(得分:6)

这不起作用,因为单身人士需要静态访问并且不能强制使用。

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

答案 1 :(得分:3)

如果要强制执行编译时检查,则无法进行此操作。通过运行时检查,您可以执行此操作它不漂亮,但它是可能的。这是一个例子:

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

答案 2 :(得分:2)

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

答案 3 :(得分:2)

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

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

接下来,允许使用受保护的构造函数,但是需要接受特殊类SingletonKey的实例,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 :(得分:1)

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

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

答案 5 :(得分:0)

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

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

请注意,如果您只需要基本的&#34;模板&#34;,您可以轻松地从通用抽象单例类中删除通用界面。

答案 6 :(得分:0)

在这里,我实现了Singleton继承:

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