Подтвердить что ты не робот

Очевидная реализация singleton для .NET?

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

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

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

Итак, я подумал о альтернативном решении, по крайней мере для .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).

EDIT для уточнения:

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

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

4b9b3361

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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*/ };
}

Ответ 6

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

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

Ответ 7

Я всегда использую этот регулярный (нелатный) один (вы можете вложить его как другие примеры): (требуется 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;
}