const vs. readonly per un singleton

3

Ho un singleton che unisce insieme diverse strutture di dati. Parte di ciò che espongo attraverso quel singleton sono alcune liste e altri oggetti, che rappresentano le chiavi o le colonne necessarie per connettere le strutture di dati collegate. Dubito che qualcuno proverebbe a cambiare questi oggetti attraverso un modulo diverso, ma voglio proteggerli esplicitamente da quel rischio. Quindi attualmente sto usando un modificatore "readonly" su quegli oggetti *.

Sto usando readonly invece di const con gli elenchi mentre leggo che l'uso di const incorporerà quegli elementi negli assembly di riferimento e quindi attiverà una ricostruzione di quegli assembly di riferimento se / quando la lista (s) è / è modificata . Questo sembra un accoppiamento più stretto di quello che vorrei tra i moduli, ma mi chiedo se sono ossessionato da un punto controverso. (Questa è la domanda n. 2 in basso)

L'alternativa che vedo usare "readonly" è rendere private le variabili e quindi includerle con un get pubblico. Sto lottando per vedere il vantaggio di questo approccio in quanto sembra un codice wrapper che non offre molti vantaggi aggiuntivi. (Questa è la domanda n. 1 in basso)

È altamente improbabile che cambiassimo il contenuto o il formato degli elenchi - sono una raccolta di cose da evitare di usare le stringhe magiche ovunque. Sfortunatamente, non tutto il codice è stato convertito per usare la presentazione di queste stringhe di singleton.

Allo stesso modo, non so che cambieremmo i contenitori / le classi per gli elenchi. Quindi, mentre normalmente sostengo i vantaggi di incapsulamento offerti da un wrapper get, in questo caso non lo sento.

Un campione rappresentativo del mio singleton

public sealed class mySingl
{
    private static volatile mySingl sngl;
    private static object lockObject = new Object();

    public readonly Dictionary<string, string> myDict = new Dictionary<string, string>()
    {
        {"I", "index"},
        {"D", "display"},
    };

    public enum parms
    {
        ABC = 10,
        DEF = 20,
        FGH = 30
    };

    public readonly List<parms> specParms = new List<parms>() 
    { 
        parms.ABC, 
        parms.FGH 
    };

    public static mySingl Instance
    {
        get
        {
            if(sngl == null)
            {
                lock(lockObject)
                {
                    if(sngl == null)
                        sngl = new mySingl();
                }
            }
            return sngl;
        }
    }

    private mySingl() 
    {
        doSomething();
    }
}

Domande:

  1. Sto prendendo l'approccio più ragionevole in questo caso?
  2. Dovrei preoccuparmi di const e readonly?
  3. c'è un modo migliore per fornire queste informazioni?

Per risolvere alcuni dubbi aggiuntivi:

Prima di tutto, capisco che ci sono persone che si oppongono all'uso dei singleton. Penso che sia un uso appropriato in questo caso poiché è un'informazione costante sullo stato, ma sono aperto a opinioni / soluzioni diverse. (Vedi Il modello singleton e Quando il pattern Singleton non dovrebbe essere utilizzato? )

In secondo luogo, per un pubblico più ampio: C ++ / CLI ha una parola chiave simile a readonly con initonly , quindi questa non è una domanda di tipo C #. ( Campo letterale rispetto a variabile costante in C ++ / CLI )

Sidenote: Una discussione su alcune delle sfumature sull'uso di const o readonly .

    
posta GlenH7 06.06.2012 - 22:28
fonte

1 risposta

2

Dato il tuo codice, ho due dubbi.

Innanzitutto, i riferimenti di sola lettura a oggetti figlio mutabili (come le raccolte) non rendono immutabili le proprietà del bambino.

public class Foo
{
   public readonly List<int> Bar = new List<int>{1,2,3};
} 

...

var foo = new Foo();

//You may enumerate Bar and read its indices
foreach(int baz in foo.Bar) Console.WriteLine(baz.ToString());

var bob = foo.Bar[1];

//this is illegal because the reference is readonly
foo.Bar = new List<int>{4,5,6};

//but the properties of the immutable reference are still mutable, so this is fine:
foo.Bar.Clear();
foo.Bar.Add(4);
foo.Bar.Add(5);
foo.Bar.Add(6);

Se vuoi un oggetto figlio veramente immutabile, devi rendere il riferimento immutabile e assicurarti che l'oggetto stesso sia immutabile. Per questo, in genere si desidera creare o utilizzare una versione di sola lettura di tuo figlio e restituirla invece:

public class Foo
{
   private readonly List<int> bar = new List<int>{1,2,3};

   public ReadOnlyCollection<int> Bar { get { return bar.AsReadOnly(); } }
} 

...

var foo = new Foo();

//You may still enumerate and index Bar
foreach(int baz in foo.Bar) Console.WriteLine(baz.ToString());

var bob = foo.Bar[1];

//this is illegal because there's no setter
foo.Bar = new List<int>{4,5,6};

//And these are also illegal because ReadOnlyCollection doesn't expose any such methods:
foo.Bar.Clear();
foo.Bar.Add(4);
foo.Bar.Add(5);
foo.Bar.Add(6);

In secondo luogo, l'istanziazione bloccata e con doppia verifica del singleton è (molto) migliore di niente per renderla thread-safe, ma ci sono ancora alcuni casi in cui ciò può fallire nel prevenire la doppia istanziazione. Microsoft consiglia invece l'uso di un campo statico di sola lettura con istanziazione definita:

public sealed class MySingl
{
   public static readonly MySingl Instance = new MySingl();

   private MySingl() { ... }
}

Microsoft garantisce che un'implementazione come questa sia thread-safe in Microsoft CLR; le statiche vengono istanziate just-in-time e il CLR ha meccanismi interni per bloccare altri thread che hanno bisogno di questo riferimento mentre viene creato.

    
risposta data 19.06.2012 - 18:27
fonte

Leggi altre domande sui tag