Очевидная одноэлементная реализация для .NET? - PullRequest
19 голосов
/ 05 июня 2009

Я думал о классической проблеме ленивой одиночной инициализации - весь вопрос неэффективности:

if (instance == null)
{
    instance = new Foo();
}
return instance;

Любой, кто знает, что такое синглтон, знаком с проблемой (вам нужно только один раз). Это банально, но раздражает.

Итак, я подумал о альтернативном решении, по крайней мере, для .NET (хотя оно должно работать где угодно у которого есть некоторый эквивалент указателям на функции:

public class Foo
{
    private delegate Foo FooReturner();

    private static Foo innerFoo;

    private static FooReturner fooReturnHandler = new FooReturner(InitialFooReturner);

    public static Foo Instance
    {
        get
        {
            return fooReturnHandler();
        }
    }
    private static Foo InitialFooReturner()
    {
        innerFoo = new Foo();
        fooReturnHandler = new FooReturner(NewFooReturner); 
        return innerFoo;
    }

    private static Foo NewFooReturner()
    {
        return innerFoo;
    }

}

Вкратце - экземпляр возвращает метод делегата. Делегат изначально настроен на метод который инициализирует ваш экземпляр, затем изменяет делегат так, чтобы он указывал на простой метод Return.

Теперь мне нравится думать, что я не ужасен на своей работе, но у меня нет претензий к тому, что я потрясающий. Я нигде не видел пример этого кода.

Эрго, я прихожу к выводу, что мне чего-то не хватает. Нечто важное. Либо, что вся проблема слишком тривиальна, чтобы думать о ней слишком много, либо это делает что-то ужасное, что разрушит вселенную. Или я не в поиске и поэтому не видел сотни разработчиков, использующих этот метод. Во всяком случае, что-то.

Я надеялся, что хорошие люди здесь, в Stack Overflow, могут подсказать мне, что (оставляя в стороне спор о том, следует ли вообще использовать Singleton).

РЕДАКТИРОВАТЬ для уточнения:

Это не код производительности (хотя, если дизайн активно снижает производительность по сравнению с традиционной моделью, было бы интересно узнать).

Он был написан исключительно как доказательство концепции, и я также понимаю, что он не является потокобезопасным, как должно быть. Есть ли какая-то причина, по которой он НЕ может быть поточно-ориентированным по самой своей природе?

Ответы [ 7 ]

31 голосов
/ 05 июня 2009

Это канонический, потокобезопасный, ленивый шаблон Singleton в C # :

public sealed class Singleton
{
    Singleton(){}
    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }        
    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested() {}    
        internal static readonly Singleton instance = new Singleton();
    }
}
10 голосов
/ 05 июня 2009

Чтобы избежать необходимости копировать одноэлементный код, вы можете сделать тип универсальным, например:

public abstract class Singleton<T>
    where T: class, new()
{
    public static T Instance
    {
        get { return Nested.instance; }
    }

    private class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested() { }

        internal static readonly T instance = new T();
    }
}

public sealed class MyType : Singleton<MyType>
{
}

class Program
{
    static void Main()
    {
        // two usage pattterns are possible:
        Console.WriteLine(
            ReferenceEquals(
                Singleton<MyType>.Instance, 
                MyType.Instance
            )
        );
        Console.ReadLine();
    }
}
4 голосов
/ 05 июня 2009

Вы измерили производительность?

Считаете ли вы, что вызов дополнительной функции дешевле, чем если?

Я согласен с остальными, что использование статического ctor хорошо работает для этой инициализации. Это также избавит от неотъемлемого состояния гонки, которое у вас есть .NET гарантирует, что статические конструкторы будут вызываться только один раз.

1 голос
/ 31 июля 2017

Лучший метод создания синглтона, который я нашел, это:

public class Singleton
{
    static public Singleton Instance { get; } = new Singleton();
    private Singleton() { ... }
}

Он очень лаконичен и, что более важно, также поточно-безопасен , что может быть очень проблематично обнаружить и исправить по мере развития приложения.


Обновление: чтобы показать, что это ленивый ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonTest
{
    class MySingleton
    {
        static public MySingleton Instance { get; } = new MySingleton();

        private MySingleton() { Console.WriteLine("Created MySingleton"); }

        public void DoSomething() { Console.WriteLine("DoSomething"); }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Main");

            MySingleton.Instance.DoSomething();
        }
    }
}

Выход:

Starting Main
Created MySingleton
DoSomething
1 голос
/ 16 мая 2011

Я только начал программировать на C # (из C ++ - глоток свежего воздуха), и это то, что я использую для реализации одноэлементных паттернов.

public sealed class MyClass
{    
    #region make singleton

    static readonly Lazy<MyClass> _singleton =
        new Lazy<MyClass>(() => new MyClass());

    public static MyClass Singleton
    {
        get { return _singleton.Value; }
    }

    private MyClass() { Initialize() };

    #endregion

    Initialize() { /*TODO*/ };
}
1 голос
/ 05 июня 2009

весь вопрос о неэффективности: ...

Какая неэффективность?

Эти инструкции приведут к очень быстрым фрагментам кода сборки. Я полностью уверен, что нечего добиваться, пытаясь «оптимизировать» это. Даже если вы придумаете что-то более быстрое, это будет сопряжено со значительными сложностями.

Если у вас нет убедительных доказательств того, что этот код влияет на вашу производительность, вы должны использовать самый простой подход, который решает вашу проблему.

0 голосов
/ 05 июня 2009

Я всегда использую этот обычный (не ленивый) (вы можете вкладывать его как другие примеры): (требуется using System.Reflection;)

public class SingletonBase<T> where T : class
{
    static SingletonBase()
    {
    }

    public static readonly T Instance = 
        typeof(T).InvokeMember(typeof(T).Name, 
                                BindingFlags.CreateInstance | 
                                BindingFlags.Instance |
                                BindingFlags.Public |
                                BindingFlags.NonPublic, 
                                null, null, null) as T;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...