Un’implementazione ovvia singleton per .NET?

Stavo pensando al classico problema dell’inizializzazione lazy singleton: tutta la questione dell’inefficienza di:

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

Chiunque sappia cos’è un Singleton ha familiarità con il problema (ne hai bisogno solo una volta). È banale ma irritante.

Così, ho pensato ad una soluzione alternativa, almeno per .NET (anche se dovrebbe funzionare ovunque che abbia qualche equivalente ai puntatori di funzione:

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

In breve: l’istanza restituisce un metodo delegato. Il delegato viene inizialmente impostato su un metodo che inizializza l’istanza, quindi modifica il delegato in modo che punti a un semplice metodo Return.

Ora, mi piace pensare di non essere terribile nel mio lavoro, ma non ho alcuna pretesa di essere fantastico. Non ho visto un esempio di questo codice ovunque.

Ergo, arrivo alla conclusione che mi manca qualcosa. Qualcosa di importante O che l’intero problema è troppo banale per preoccuparsi di pensarci tanto o questo fa qualcosa di orribile che distruggerà l’universo. Oppure non riesco a cercare e quindi non ho visto le centinaia di sviluppatori che usano questo metodo. Qualcosa, comunque.

Speravo che i bravi ragazzi qui a Stack Overflow potessero identificarmi su cosa (a parte le polemiche sull’opportunità o meno di usare un Singleton).

MODIFICA per chiarimenti:

Questo non è un codice di prestazioni (anche se il design degrada triggersmente le prestazioni oltre il modello tradizionale, sarebbe interessante sapere).

È stato scritto puramente come proof-of-concept, e sono inoltre consapevole che non è thread-safe come dovrebbe essere correttamente. C’è qualche ragione per cui NON potrebbe essere reso thread-safe per la sua natura?

Questo è il pattern Singleton canonico, thread safe e pigro in 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(); } } 

Per evitare di dover copiare il codice singleton, è ansible rendere generico il tipo, in quanto tale:

 public abstract class Singleton 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 { } class Program { static void Main() { // two usage pattterns are possible: Console.WriteLine( ReferenceEquals( Singleton.Instance, MyType.Instance ) ); Console.ReadLine(); } } 

Hai misurato la performance?

Credi che una chiamata di funzione aggiuntiva sia più economica di un if?

Sono d’accordo con gli altri che l’uso del ctor statico funziona bene per questa inizializzazione. Ciò eliminerà anche la condizione di razza inerente che hai da allora. Net garantisce che i costruttori statici saranno chiamati una sola volta.

tutta la questione dell’inefficienza di: …

Che inefficienza?

Queste istruzioni si tradurranno in un frammento estremamente veloce di codice assembly. Sono assolutamente sicuro che non ci sia nulla da guadagnare cercando di “ottimizzare” questo. Anche se ti viene in mente qualcosa di più veloce, sarà a un costo significativo della complessità.

A meno che tu non abbia prove positive del fatto che questo codice influenzi le tue prestazioni, dovresti utilizzare l’approccio più semplice che risolve il tuo problema.

Ho appena iniziato a programmare in C # (proveniente da C ++ è una boccata d’aria fresca) e questo è quello che uso per implementare modelli singleton.

 public sealed class MyClass { #region make singleton static readonly Lazy _singleton = new Lazy(() => new MyClass()); public static MyClass Singleton { get { return _singleton.Value; } } private MyClass() { Initialize() }; #endregion Initialize() { /*TODO*/ }; } 

Il miglior metodo che ho trovato di creare un singleton è questo:

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

È molto conciso e, cosa più importante, è anche thread-safe , che può essere molto fastidioso da rilevare e correggere con la scadenza di un’applicazione.


Aggiornamento: per mostrare che è pigro …

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

Produzione:

 Starting Main Created MySingleton DoSomething 

Uso sempre questo tipo regolare (non pigro) (è ansible annidarlo come gli altri esempi): (richiede l’ using System.Reflection; )

 public class SingletonBase 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; }