Як абстрактным одноплодной клас?

Гэта, як я пішу одноэлементные класы.

public class MyClass
{
    /// 
/// Singleton ///
 
    private static MyClass instance;

    /// 
/// Singleton access. ///
 
    public static MyClass Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new MyClass();
            }
            return _instance;
        }
    }

    private MyClass() { .... }
}

Як стварыць Singleton шаблон, які можна выкарыстоўваць паўторна?

Singleton мадэль уяўляе наступныя праблемы.

  • Канструктар прыватным або абаронены .
  • Базавы клас не можа стварыць асобнік спадчыну класа. Такім чынам, вы можаце выкарыстоўваць агульны абстрактны MyAbstractSingletonClass .
  • Ён павінен мець лакальнае ўласцівасць толькі для чытання, каб атрымаць асобнік.

Праблема

Я выкарыстоўваю гэты шаблон на некалькі класаў і заўсёды прыходзіцца пісаць адзін і той жа код. Як я магу напісаць нешта, што паўторна выкарыстоўваецца кожны раз, калі мне патрэбен Сінглтан?

25
@Gjeltema адрэдагаваць маё пытанне. Надзея, што дапамагае.
дададзена аўтар cgTag, крыніца
Я спрабую зразумець, што вы спрабуеце дасягнуць тут. Вы хочаце, каб базавы клас, каб мець веданне вытворнага класа, і вярнуць тое, ці ты хацеў базавы клас проста «ведаць» как-то вярнуць толькі клас, вытворны ад яго? Вы можаце дадаць статычную функцыю базавага класа з імем Initialize (тып радка) ці нешта сказаць, што базавы клас для ініцыялізацыі аднаго асобніка з? Трохі больш тлумачэнняў аб тым, што вы спрабуеце зрабіць, і што вашыя абмежаванні дапамогуць.
дададзена аўтар Gjeltema, крыніца
Так, гэта так, але BTownTKD біць мяне друкуючы адказ. :)
дададзена аўтар Gjeltema, крыніца

8 адказы

Вы можаце дасягнуць гэтага, выкарыстоўваючы камбінацыю автореферентное універсальнага тыпу абмежаванняў , і" новы() " тып абмежаванні.

«Новы» абмежаванне гарантуе, што любы клас дзіця заўсёды будзе мець канструктар без параметраў, так што _instance = новы T (); будзе заўсёды працаваць.

Тып автореферентно абмежаванне гарантуе, што «Instance» статычнае ўласцівасць заўсёды вяртае правільны тып; ня тып «база». Ваш Одноточечный базавы клас будзе выглядаць прыкладна так:

public abstract class SingletonBase 
    where T : SingletonBase, new()
{
    private static T _instance = new T();
    public static T Instance
    {
        get
        {                
            return _instance;
        }   
    }
}

Вашы класы дзіця будзе выглядаць наступным чынам:

public class MyChildSingleton : SingletonBase
{
    //Done!
}

Вядома, калі вы хочаце, каб ваш Одноточечный быць агульнага прызначэння, вы павінны таксама змяніць «стварыць одноэлементный асобнік» код трохі, каб выкарыстоўваць « двайны Праверка блакавання " шаблон, ці Гультаяватыя клас, каб зрабіць яго потокобезопасна.

The big caveat: if you use this method, the "new()" constraint pretty much ensures that your class will always have a public, parameterless constructor. That means your end-users could always just call new MyChildSingleton() if they really wanted, bypassing your singleton instance entirely. Your singleton would be "by convention," instead of strictly enforced. To get around this would take a bit more engineering. In the above scenario, the convention seems to be that you should name your static instance "Default" instead of "Instance." This subtly conveys the fact that your class offers a 'suggested' singleton instance, but using it is technically optional.

Я зрабіў некалькі спробаў строга сачыць за выкананнем патэрна адзіночка, і канчатковы вынік быў выкарыстоўваць адлюстраванне, каб ўручную выклікаць прыватны канструктар. Вы можаце ўбачыць мой поўны код спробы .

45
дададзена
Гэта на самай справе даволі проста абмежаваць выклік канструктара падчас выканання (не ўпэўнены, што магчымы ў кампіляцыі). Усё, што вам зрабіць, гэта дадаць абаронены канструктар ў SingletonBase, што выклікае выключэнне, калі _instance ня роўнае нулю. Будзе згенеравана выключэнне, нават калі канструктар з'яўляецца першым, што трэба быць выкліканыя звонку.
дададзена аўтар Eugene Marin, крыніца
Гэтыя тыпы не адзінкі. Вы павінны мець адкрыты канструктар па гэтых тыпах, каб задаволіць гэта радавое абмежаванне, так што кожны можа ствараць новыя асобнікі з іх, што робяць іх не адзіночка.
дададзена аўтар Servy, крыніца
@MathewFoscarini - калі ласка, праверце артыкул Джона скіта на Адзіночкі, перш чым выкарыстоўваць гэты код, як гэта: csharpindepth.com/ артыкулы/Агульныя/Singleton.aspx , паколькі гэта можа запатрабаваць некаторых шматструменных рыскі і можа атрымаць выгаду з Lazy класа.
дададзена аўтар Alexei Levenkov, крыніца

Даданне адказу BTownTKD, гэта на самай справе даволі проста абмежаваць выклік канструктара падчас выканання (не ўпэўнены, што магчымы ў кампіляцыі). Усё, што вам зрабіць, гэта дадаць абаронены канструктар ў SingletonBase, што выклікае выключэнне, калі _instance ня роўнае нулю. Будзе згенеравана выключэнне, нават калі канструктар з'яўляецца першым, што трэба быць выкліканыя звонку.

I managed to apply this technic in a singleton base, and also make it lazy and thread safe as described here: http://csharpindepth.com/Articles/General/Singleton.aspx

У выніку (з выкарыстаннем тлумачэння):

/// 
/// Generic singleton class, providing the Instance property, and preventing manual construction. /// Designed as a base for inheritance trees of lazy, thread-safe, singleton classes. /// Usage: /// 1. Sub-class must use itself, or its sub-class, as the type parameter S. /// 2. Sub-class must have a public default constructor (or no constructors). /// 3. Sub-class might be abstract, which requires it to be generic and demand the generic type /// have a default constructor. Its sub-classes must answer all these requirements as well. /// 4. The instance is accessed by the Instance getter. Using a constructor causes an exception. /// 5. Accessing the Instance property in an inner initialization in a sub-class constructor /// might cause an exception is some environments. ///
 
/// Lowest sub-class type.
public abstract class Singleton where S : Singleton, new()
{
    private static bool IsInstanceCreated = false;
    private static readonly Lazy LazyInstance = new Lazy(() =>
        {
            S instance = new S();
            IsInstanceCreated = true;
            return instance;
        });

    protected Singleton()
    {
        if (IsInstanceCreated)
        {
            throw new InvalidOperationException("Constructing a " + typeof(S).Name +
                " manually is not allowed, use the Instance property.");
        }
    }

    public static S Instance
    {
        get
        {
            return LazyInstance.Value;
        }
    }
}

Я павінен сказаць, што я не зрабіў інтэнсіўнае тэставанне шматструменнасці, але некаторыя з іх ужо сказалі, вы заўсёды можаце выкарыстоўваць стары двайную праверку трук.

4
дададзена
Дзякуем Вам за гэта! Я выявіў, што крыху падправіць, каб прадухіліць стварэнне асобніка ўручную. IsInstanceCreated = ісціна павінна была быць ўстаноўлена да новага S (), і InvalidOperationException прыйшлося выкінуць, калі! IsInstanceCreated. У адваротным выпадку ён працаваў толькі пасля таго, як одноэлементно была створана, але калі клас быў асобнік ўручную першым.
дададзена аўтар Jamieson Rhyne, крыніца

Вы правільна - як гэта ў цяперашні час стаіць, вы не можаце гэтага дамагчыся. Але вы можаце падысці да яго з дапамогай дженеріков, звярніце ўвагу, што пры такім падыходзе вы атрымаеце <�моцны> адзін одноэлементный асобнік для кожнага унікальнага вытворнага тыпу :

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var x = new MyDerivedClass();
            Console.WriteLine(x.ToString());
            Console.WriteLine(x.Instance.ToString());

            Console.ReadKey();
        }
    }


    public abstract class MyBaseClass where T : class, new()
    {
        protected T GetInstance()
        {
            if (_instance == null)
            {
                lock (_lockObj)
                {
                    if (_instance == null)
                        _instance = new T();
                }
            }
            return _instance;
        }

        public T Instance
        {
            get { return GetInstance(); }
        }

        private volatile static T _instance;
        private object _lockObj = new object();
    }

    public class MyDerivedClass : MyBaseClass
    {
        public MyDerivedClass() { }
    }

}
3
дададзена
Вашы вытворныя класы ўсе павінен ёсць адкрыты канструктар, гэта значыць тых, хто хоча ствараць новыя асобнікі, а гэта азначае, што яны не адзіночак, так як там можа быць больш, чым адзін асобнік з іх.
дададзена аўтар Servy, крыніца

Сапраўднае рашэнне пачынаецца з падыходам BTownTKD, але павялічваючы яго з дапамогай метаду Activator.CreateInstance, які дазваляе дзіця класах трымаць закрытыя канструктар.

<�Моцны> бацькоўскі клас </моцны>

public abstract class BaseSingleton where T :
    BaseSingleton
{
    private static readonly ThreadLocal Lazy =
        new ThreadLocal(() =>
            Activator.CreateInstance(typeof(T), true) as T);

    public static T Instance => Lazy.Value;
}

<�Моцны> Дзіцячы клас

public sealed class MyChildSingleton : BaseSingleton
{
    private MyChildSingleton() { }
}

Поўны прыклад рэалізацыі Тут

3
дададзена
Я не ведаю, чаму гэта не агульнапрыняты адказ або чаму гэта яшчэ не атрымаў upvotes. Адзінае абмежаванне, я думаю, для гэтага рашэння з'яўляецца тое, што наяўнасць прыватнага без параметраў-канструктарам можа быць забяспечана толькі падчас выканання, а не падчас кампіляцыі. Акрамя гэтага, тым не менш, гэта, безумоўна, самым магутным рашэннем пералічаных тут.
дададзена аўтар Teun Kooijman, крыніца

Просты адказ заключаецца ў тым, што вы не можаце рэалізаваць одноплодной мадэль у базавым класе.

Тым не менш, вы можаце рэалізаваць іншыя мадэлі Тварэнні канструкцыі, якія могуць быць прыдатныя для таго, што вы спрабуеце дасягнуць. Напрыклад, зірніце на Abstract Factory .

2
дададзена

Радавое, абцякальны, ўкараненне поточно- беззамочный шаблон :

public abstract class Singleton where T : class, new() {
    public static readonly T Instance = new T();
}
<�Р> Статычны канструктар выключаны на карысць прадукцыйнасці, бо лянота не патрабуецца, а ўласцівасць замяняецца грамадскім полем для сцісласці

<�Моцны> Рэалізацыя

public sealed class Foo : Singleton {
    public void Bar() {
        //...
    }
}

<�Моцны> Выкарыстанне

Foo.Instance.Bar();
1
дададзена

<�Моцны> Мой рэкамендавалася прыклад:

базавы клас

public abstract class SingletonBase where T : class
{
  private static readonly Lazy sInstance = new Lazy(() => CreateInstanceOfT());

  public static T Instance { get { return sInstance.Value; } }

  private static T CreateInstanceOfT()
  {
    return Activator.CreateInstance(typeof(T), true) as T;
  }
}

Выкарыстанне

public class yourClass : SingletonBase
{
   public yourMethod()
   {
   }
}

выкарыстоўваць одноэлементный клас, як гэта:

yourClass.Instance.yourMethod();

for more info see my answer source in this link

0
дададзена

Нядаўна я прапанаваў гэты адказ на адпаведнае пытанне:

https://stackoverflow.com/a/20599467

З дапамогай гэтага метаду, базавы клас кіруе стварэннем ўсіх экзэмпляраў вытворных класаў, так як усе вытворныя канструктары класа патрабуе аб'екта, які можа забяспечыць толькі базавы клас, і няма неабходнасці ў абмежаванні канструктары без параметраў.

0
дададзена
Сапраўды, для класа SingleTone, ён павінен мець прыватны канструктар без параметраў. Зірніце csharpindepth.com/Articles/General/Singleton.aspx
дададзена аўтар M.Hassan, крыніца