الفئة الأساسية مجردة إلى قوة كل الفئات المشتقة إلى المفرد

StackOverflow https://stackoverflow.com/questions/2855245

  •  27-09-2019
  •  | 
  •  

سؤال

كيف يمكنني جعل فئة مجردة التي يجب قوة كل الفئات المشتقة إلى المفرد ?يمكنني استخدام C#.

هل كانت مفيدة؟

المحلول

عندما ترغب في التحقق من وقت التجميع ، فإن هذا غير ممكن. مع التحقق من وقت التشغيل يمكنك القيام بذلك. إنها ليست جميلة ، لكنها ممكنة. هذا مثال:

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

نصائح أخرى

لن ينجح ذلك لأن المفرد يحتاج إلى حد ما إلى وصول ثابت ولا يمكن إجباره.

للاطلاع على SingletOnimplemention + أمثلة انظر: تنفيذ نمط Singleton في C#

Singleton يعني وجود مُنشئين خاصين. لكنك تعلم أن الأعضاء من القطاع الخاص لا يمكن أن يتم مورثهم. في C ++ كانت هناك قوالب ، حتى تتمكن من إنشاء مفردة من فئة قالب. في C#، لا توجد قوالب ، لذلك عليك كتابة مُنشئاتك الخاصة لكل مفردة تريدها.

إليك طريقة (قبيحة) للقيام بذلك. ربما يمكن تبسيطها وتحسينها ، لكنها أول ذهاب لي.

تتمثل الفكرة أولاً في جعل الفئة التجريدية العامة من الفئة A (كما هو مذكور في التعليقات أعلاه) ، لكن المعلمة النوع مقيدة لاستخلاصها من الفئة الأساسية نفسها. هذا يسمح للفئة الأساسية بالتعامل مع مثيل Singleton من النوع المشتق. لاحظ أنه ينبغي إغلاق جميع الفصول المشتقة ، كما هو الحال مع أي فئة مفردة.

بعد ذلك ، يُسمح بمنشئ محمي ، ولكنه مطلوب لقبول مثيل لصف خاص ، 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)
    {
    }
}

دروس في جافا أو C# لا "من الدرجة الأولى".ساكنة جزء من الطبقة لا يمكن أن تكون موروثة أو تجاوز من قبل الفئات الفرعية.انظر هذا الجواب للحصول على مزيد من التفاصيل.بالإضافة إلى أنك لا تملك مفهوم الطبقة الفوقية.

في لغات مثل Smalltalk أو روبي, يمكنك تعريف جديد metaclass Singleton الذي يحدد طريقة getInstance.ثم يمكنك تحديد ClassA و ClassB حالات من Singleton metaclass.ثم كل الطبقات تلقائيا فضح طريقة getInstance والتي يمكن استخدامها لخلق حالات objectA أو objectB.أليس هذا رائعا ؟ حسنا, في الواقع كنت لا تستخدم metaclass في كثير من الأحيان, و المفرد هو في الواقع فقط من الاستخدام منهم أن من المنطقي أن أنا على علم.

أعتقد أنني حاولت تحقيق شيء مماثل ، أي فرض واجهة مشتركة ونمط Singleton على مجموعة من الفصول. كان هذا الحل:

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

يرجى ملاحظة أنه يمكنك بسهولة إزالة الواجهة المشتركة من فئة 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";
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top