Очевидная реализация 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 для уточнения:
Это не код производительности (хотя, если дизайн активно снижает производительность за пределами традиционной модели, это было бы интересно узнать).
Это было написано просто как доказательство концепции, и я также отдаю себе отчет в том, что он не является потокобезопасным, каким он должен быть. Есть ли какая-то причина, по которой она не может быть потокобезопасной по своей природе?
Ответы
Ответ 1
Это канонический, потокобезопасный, ленивый шаблон Singleton в С#:
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();
}
}
Ответ 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;
}