2009-07-06 5 views
7

Ho una domanda sui singleton che penso di conoscere la risposta a ... ma ogni volta che lo scenario si apre mi viene in qualche modo da indovinare un po ', quindi vorrei piacerebbe conoscere la risposta concreta.Domanda su membri non statici di un singleton (C#)

Dire Ho installato due classi in modo ...

public class ClassA 
{ 
    private static ClassA _classA; 

    public static ClassA Instance { get { return _classA ?? LoadClassA(); } } 

    private ClassA(){} 

    public static ClassA LoadClassA() 
    { 
     _classA = new ClassA(); 
     return _classA; 
    } 

    private ClassB _classB = new ClassB(); 

    public ClassB ClassB { get { return _classB; } set { _classB = value; } } 
} 


public class ClassB 
{ 
} 

mia domanda è semplice.

Mi chiedo se il campo _classB sia considerato statico anche se accedo al singleton per ClassA? Anche se non ho dichiarato _classB come membro statico.

Ho sempre immaginato che _classB sia considerato statico (una posizione di memoria), ma mi piacerebbe sapere con certezza. Ho sbagliato? È un nuovo oggetto creato per _classB ogni volta che lo si accede da Singleton ClassA ... anche se c'è solo un ClassA in memoria? O è perché ho aggiornato _classB sulla dichiarazione che fa sì che ci sia solo una sua istanza?

Grazie in anticipo, -Matt

+0

ci sarà certamente un'istanza di _classB. –

risposta

11

Quando si crea un singleton, si crea una singola istanza statica di tipo non statico.

In questo caso, il tipo (Classe A) contiene un riferimento a un altro tipo (Classe B). L'istanza statica contiene un singolo riferimento a una singola istanza di un oggetto di classe B. Tecnicamente, non è "statico", ma dal momento che è radicato su un oggetto statico (l'istanza di classe A), si comporterà come una variabile statica. Avrai sempre un solo oggetto di Classe B (indicato dalla tua istanza di Classe A). Non creerai mai più di un'istanza di classe B dall'interno della classe A.

Non c'è nulla, tuttavia, che impedisca la generazione di un'altra istanza di Classe B altrove - questa sarebbe un'istanza diversa.

+4

A meno che non vedo qualcosa di sbagliato qui, dovresti anche creare un costruttore privato per 'ClassA'. Se non lo fai, non ci sarà nulla che impedisca al codice esterno di creare una nuova istanza di 'ClassA', che significherà più istanze di' ClassB' (poiché ogni 'ClasseA' avrà copie indipendenti). –

+0

Mi ha anche sconcertato, ma ho pensato che lo fosse solo perché sono circa le 2 del mattino :) – TheVillageIdiot

3

_classB è un esempio (non statico) membro di ClassA. Ogni istanza di ClassA avrà un'istanza di _classB (dato l'inizializzatore di campo che hai scritto). Quindi, se stai utilizzando il pattern Singleton per accedere a ClassA e quindi hai sempre (al massimo) un'istanza di ClassA caricata, avrai sempre (al massimo) un'istanza di ClassB caricata tramite ClassA.

Ora, poiché ClassB ha un costruttore pubblico predefinito, qualcos'altro potrebbe creare istanze da solo. Se questo è un problema, considera la possibilità di rendere privata la classe ClassB. Inoltre, dal momento che ClassA ha un costruttore pubblico predefinito, nulla impedisce alla gente di creare tutte le istanze che vogliono. È possibile rendere privato il costruttore no-arg:

private ClassA() {} 
1

Come definito ClassA viola la definizione per singleton. Immagina due thread contemporaneamente chiama la proprietà statica di istanza. Poiché l'accesso non è sincronizzato, è possibile ottenere con due diverse istanze di ClassA e quindi due diverse istanze di ClassB.

  1. Filettatura1 chiama struttura grado e come _classA è nullo entra nel metodo LoadClassA
  2. Thread2 chiama struttura grado e come _classA rimane a NULL entra nel metodo LoadClassA
  3. Filettatura1 e Thread2 ottenere due diverse istanze di ClasseA
+0

Non sono sicuro che "Violetta la definizione", come in una singola applicazione con thread, andrebbe bene. Ma ci sono preoccupazioni sulla sicurezza del filo come hai sottolineato. – NerdFury

2

il pattern singleton assicura che solo un'istanza di ClassB è accessibile dalla istanza singleton ClassA in ogni momento S. Il punto principale del modello singleton è che è che garantisce solo un'istanza di ClassA disponibile in qualsiasi momento, quindi un solo riferimento a _classB (sebbene dal ClassA sia modificabile, questo riferimento può cambiare).

Si noti tuttavia che lo scope di ClassB è ancora a livello di istanza e non a livello statico. Il compilatore sarà mai fare qualcosa di così strano da utilizzare un identificatore di ambito diverso da quello che si indica.È comunque necessario accedere al riferimento a ClassB tramite un'istanza, indipendentemente dal fatto che si stia utilizzando o meno un singleton.

2

Avendo la tua dichiarazione come questa:

private ClassB _classB = new ClassB(); 

Stai istanziare _classB a una nuova istanza di ClassB ogni volta che viene chiamato il costruttore di ClassA.

Con un modello singleton, l'unico modo per chiamare il costruttore di ClassA consiste nell'utilizzare un metodo statico (nel tuo caso tramite la proprietà Instance), che garantisce in modo efficace che venga creata solo una ClasseA.

Ciò garantisce che _classB venga aggiornato solo una volta, ma non è statico. Tuttavia, se qualcuno ha cambiato ClassA per non essere più un singleton in futuro, allora inizieresti a creare più istanze di ClassB. Se _classB fosse veramente statico, questo non sarebbe il caso.

0

Perché non basta contrassegnare ClassB come statico se si desidera solo un'istanza.

A mio parere è sempre meglio scrivere codice chiaro che mostri le intenzioni piene. In questo modo la prossima persona a lavorare sul tuo codice non deve giocare a un gioco di ipotesi per determinare cosa volevi fare.

+0

Contrassegnare una classe come statica rende impossibile istanziare qualsiasi istanza di quella classe. Chiaramente, sono richieste istanze di ClassA e ClassB. –

0

Poiché ClassA è un singleton, nell'applicazione è presente solo un'istanza. All'interno di quell'istanza della classe, c'è un membro per memorizzare una variabile ClassB. Una volta inizializzata questa variabile, si tratta di una variabile di istanza, ma dal momento che hai solo 1 istanza di ClassA, otterrai sempre la stessa istanza di ClassB.

Inoltre, consiglierei contro il tuo modo di creare l'istanza interna in quanto non è thread-safe.

private static ClassA _instance = new ClassA(); 

private ClassA() {} 

public static ClassA Insance { get { return _instance; } } 

da caricamento non pigro e inizializzando subito, si garantisce la sicurezza del filo.

0

_classB non è statico; un'istanza di ClassA contiene un riferimento a un'istanza di _classB; se l'istanza di ClassA è statica (come nell'istanza Singleton), allora conterrà ancora un riferimento a _classB, ma le nuove istanze di ClassA conterranno riferimenti DIVERSI a (potenzialmente) diverse istanze di _classB. Quindi, se si fa riferimento alla stessa istanza di ClassA (attraverso il modello singleton), si otterrà comunque la stessa istanza di _classB; ma se crei una nuova istanza di ClassA, otterrai una diversa istanza di _classB, motivo per cui _classB non è statico.

0

Sono sorpreso che nessuno ha sottolineato il fatto che ClassB è una proprietà pubblica di ClassA con un setter pubblico.Come tale, nulla vieta a chiunque di scrivere qualcosa di simile:

ClassA.Instance.ClassB = new ClassB(); 

questo sarebbe scartare l'istanza di ClassB creato al momento ClassA s' esempio solitaria è stato creato e avrebbe sostituirlo con il nuova istanza di ClassB.

Uno scenario correlato è che, fintanto che _classB è impostabile in una classe derivata (ad esempio, se il setter per ClassB è stata fatta protetta), non ci può essere alcuna garanzia che ci sarebbe solo una singola istanza di ClassB mai.

Suppongo che il modo corretto di scrivere questo tipo di codice sarebbe quello di implementare sia ClassA e ClassB come single e fare ClassB una classe interna di ClassA (altrimenti si sarebbe 2 classi non correlate ClassA e ClassB con un riferimento a ClassB disponibili attraverso ClassA.Instance.ClassB, che (in questo scenario) essere differente da ClassB.Instance.

Se ClassB era solo istanziabile all'interno ClassA, quindi potenzialmente ci potrebbero essere altre istanze di ClassB instanziabile in una classe derivata.