Indicizzatori statici?


119

Perché gli indicizzatori statici non sono consentiti in C #? Non vedo motivo per cui non dovrebbero essere consentiti e inoltre potrebbero essere molto utili.

Per esempio:

public static class ConfigurationManager 
{
        public object this[string name]
        {
            get => ConfigurationManager.getProperty(name);
            set => ConfigurationManager.editProperty(name, value);
        }

        /// <summary>
        /// This will write the value to the property. Will overwrite if the property is already there
        /// </summary>
        /// <param name="name">Name of the property</param>
        /// <param name="value">Value to be wrote (calls ToString)</param>
        public static void editProperty(string name, object value) 
        {
            var ds = new DataSet();
            var configFile = new FileStream("./config.xml", FileMode.OpenOrCreate);
            ds.ReadXml(configFile);

            if (ds.Tables["config"] == null)
                ds.Tables.Add("config");

            var config = ds.Tables["config"];

            if (config.Rows[0] == null) 
                config.Rows.Add(config.NewRow());

            if (config.Columns[name] == null) 
                config.Columns.Add(name);

            config.Rows[0][name] = value.ToString();

            ds.WriteXml(configFile);
            configFile.Close();
        }

        public static void addProperty(string name, object value) =>
            ConfigurationManager.editProperty(name, value);

        public static object getProperty(string name) 
        {
            var ds = new DataSet();
            var configFile = new FileStream("./config.xml", FileMode.OpenOrCreate);
            ds.ReadXml(configFile);
            configFile.Close();

            if (ds.Tables["config"] == null) return null;

            var config = ds.Tables["config"];

            if (config.Rows[0] == null) return null;
            if (config.Columns[name] == null) return null;

            return config.Rows[0][name];
        }
    }

Il codice precedente trarrebbe grandi vantaggi da un indicizzatore statico. Tuttavia non verrà compilato perché gli indicizzatori statici non sono consentiti. Perché è così?


Successivamente voglio l'implementazione diretta di IEnumerable sulla classe statica, quindi posso farlo foreach (var enum in Enum):)
nawfal

Risposte:


72

La notazione dell'indicizzatore richiede un riferimento a this. Poiché i metodi statici non hanno un riferimento a nessuna particolare istanza della classe, non è possibile utilizzarli thiscon essi e di conseguenza non è possibile utilizzare la notazione dell'indicizzatore sui metodi statici.

La soluzione al tuo problema sta usando un pattern singleton come segue:

public class Utilities
{
    private static ConfigurationManager _configurationManager = new ConfigurationManager();
    public static ConfigurationManager ConfigurationManager => _configurationManager;
}

public class ConfigurationManager
{
    public object this[string value]
    {
        get => new object();
        set => // set something
    }
}

Ora puoi chiamare Utilities.ConfigurationManager["someKey"]usando la notazione dell'indicizzatore.


110
Ma perché l'indicizzatore deve usare "this"? Non è necessario accedere ai dati dell'istanza
Malfist

80
+1 per il commento di Malfist. Solo perché usa "this" per un indicizzatore di istanze non significa che non possano trovare un'altra sintassi.
Jon Skeet

40
Concordato. Stai implorando la domanda. In pratica hai detto che il motivo per cui non è consentito è perché non è consentito. -1 perché la domanda era "perché non è consentito?"
xr280xr

15
@ xr280xr +1 Per un uso corretto di "supplicare la domanda" :) Inoltre ho la stessa lamentela.
RedFilter

14
-1 perché questa risposta presume che la notazione corrente sia l'unica notazione possibile se fosse implementato un indicizzatore statico. L'uso di thisin un indicizzatore non è necessariamente richiesto, è stato probabilmente scelto sopra altre parole chiave perché aveva più senso. Per un'implementazione statica, la seguente sintassi può essere molto valida: public object static[string value]. Non è necessario utilizzare la parola chiave thisin un contesto statico.
einsteinsci

91

Credo che non sia stato considerato particolarmente utile. Penso che sia anche un peccato: un esempio che tendo a usare è Encoding, dove Encoding.GetEncoding("foo")potrebbe essere Encoding["Foo"]. Non penso che succederebbe molto spesso, ma a parte qualsiasi altra cosa sembra un po 'incoerente non essere disponibile.

Dovrei controllare, ma sospetto che sia già disponibile in IL (Intermediate Language).


6
Intermediate Language - una sorta di linguaggio assembly per .NET.
Jon Skeet

15
Ciò che mi ha portato qui è che ho una classe personalizzata che espone un dizionario di valori comuni utilizzati in tutta la mia applicazione tramite una proprietà statica. Speravo di utilizzare un indicizzatore statico per abbreviare l'accesso da GlobalState.State [KeyName] a GlobalState [KeyName]. Sarebbe stato carino.
xr280xr

1
FWIW, il passaggio instancea staticin IL per una proprietà e il metodo getter su una proprietà predefinita si traduce in un reclamo per ilasm syntax error at token 'static'; Non sono bravo a immischiarmi negli affari di IL, ma suona almeno come un no iniziale.
Amazingant

8

Per ovviare al problema, puoi definire un indicizzatore di istanze su un oggetto singleton / statico (supponiamo che ConfigurationManager sia un singleton, invece di essere una classe statica):

class ConfigurationManager
{
  //private constructor
  ConfigurationManager() {}
  //singleton instance
  public static ConfigurationManager singleton;
  //indexer
  object this[string name] { ... etc ... }
}

1

Avevo anche bisogno (beh, più bello da avere) di un indicizzatore statico per memorizzare gli attributi, quindi ho trovato una soluzione alquanto imbarazzante:

All'interno della classe che desideri avere un indicizzatore statico (qui: Element), crea una sottoclasse con lo stesso nome + "Dict". Dagli un valore statico di sola lettura come istanza di detta sottoclasse, quindi aggiungi l'indicizzatore desiderato.

Infine, aggiungi la classe come importazione statica (da qui la sottoclasse per esporre solo il campo statico).

import static Element.ElementDict;

public class Element {
    // .... 
    private static readonly Dictionary<string, object> elemDict = new Dictionary<string, object>();
    public class ElementDict {
        public readonly static ElementDict element = new ElementDict();
        public object this[string key] {
            get => elemDict.TryGetValue(key, out object o) ? o : null;
            set => elemDict[key] = value;
        }
    }
}

e quindi puoi usarlo con le maiuscole come Tipo o senza come dizionario:

var cnt = element["counter"] as int;
element["counter"] = cnt;

Ma ahimè, se si utilizzasse effettivamente object come "value" -Type, il seguente sarebbe ancora più breve (almeno come dichiarazione) e fornirebbe anche un Typecasting immediato:

public static T load<T>(string key) => elemDict.TryGetValue(key, out object o) ? (T) o : default(T);
public static void store<T>(string key, T value) => elemDict[key] = value;

var cnt = Element.load<int>("counter");
Element.store("counter", cnt);

0

Con i costrutti più recenti in C # 6, potresti semplificare il modello singleton con un corpo di espressione di proprietà. Ad esempio, ho usato la seguente scorciatoia che funziona bene con code-lense:

public static class Config
{
   public static NameValueCollection Get => ConfigurationManager.AppSettings;
}

Ha l'ulteriore vantaggio di essere in grado di trovare e sostituire per aggiornare il codice precedente e unificare l'accesso alle impostazioni dell'applicazione.


-2

La parola chiave this si riferisce all'istanza corrente della classe. Le funzioni membro statiche non hanno un puntatore this. La parola chiave this può essere utilizzata per accedere ai membri dall'interno di costruttori, metodi di istanza e funzioni di accesso alle istanze (recuperato da msdn ). Poiché fa riferimento a un'istanza della classe, è in conflitto con la natura di static, poiché static non è associato a un'istanza della classe.

Una soluzione alternativa potrebbe essere la seguente che ti consente di utilizzare l'indicizzatore su un dizionario privato, quindi devi solo creare una nuova istanza e accedere alla parte statica.

    public class ConfigurationManager 
{
    public ConfigurationManager()
    {
        // TODO: Complete member initialization
    }
    public object this[string keyName]
    {
        get
        {
                return ConfigurationManagerItems[keyName];
        }
        set
        {
                ConfigurationManagerItems[keyName] = value;
        }
    }
    private static Dictionary<string, object> ConfigurationManagerItems = new Dictionary<string, object>();        
}

Ciò consente di saltare l'intero accesso a un membro della classe e di crearne semplicemente un'istanza e indicizzarla.

    new ConfigurationManager()["ItemName"]

4
è una soluzione alternativa interessante, ma 1) introduce effetti collaterali (creazione di un oggetto istanza vuoto) che potrebbero portare alla pressione della memoria e alla frammentazione in alcuni ambienti, 2) i caratteri extra sprecati da new ()potrebbero essere stati usati per il nome del qualificatore di un singleton invece, come.Current
Lawrence Ward

1
Proprio come la risposta di Juliet , questa non risponde alla domanda sul perché gli indicizzatori statici non sono supportati. In primo luogo, la domanda non limita il termine "indicizzatore statico" a "qualcosa che utilizza la thisparola chiave", e in secondo luogo, thisnella sintassi in public string this[int index]senso stretto non è nemmeno un uso di un thispuntatore (come può verificarsi nel corpo dei metodi di istanza) , ma solo un altro uso del token this . La sintassi public static string this[int index]potrebbe sembrare un po 'controintuitiva, ma sarebbe comunque non ambigua.
OR Mapper

2
@ORMapper Potrebbe anche essere public static string class[int index].
Jim Balter

Immagino di essere confuso, ho pensato "La parola chiave this si riferisce all'istanza corrente della classe. Le funzioni membro statiche non hanno un puntatore this. ' stava spiegando che poiché non esiste alcun oggetto a cui fare riferimento il puntatore this, non è possibile utilizzare il riferimento ad esso. E ho anche affermato che msdn è stato colui che usa quella definizione. la stringa statica pubblica e la stringa pubblica non si sovrapporranno mai, per quanto ne so, poiché una accede all'oggetto di tipo generico, mentre l'altra accede a un oggetto istanza.
lamorach

-2

Il motivo è perché è abbastanza difficile capire cosa stai indicizzando esattamente con un indicizzatore statico.

Dici che il codice trarrebbe vantaggio da un indicizzatore statico, ma lo sarebbe davvero? Tutto ciò che farebbe è cambiare questo:

ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)

In questo:

ConfigurationManager[name] = value
...
value = ConfigurationManager[name]

che non migliora in alcun modo il codice; non è più piccolo di molte righe di codice, non è più facile da scrivere grazie al completamento automatico ed è meno chiaro, poiché nasconde il fatto che stai ottenendo e impostando qualcosa che chiami 'Proprietà' e in realtà costringe il lettore a vai a leggere la documentazione su cosa esattamente restituisce o imposta l'indicizzatore, perché non è in alcun modo ovvio che si tratti di una proprietà per la quale stai indicizzando, mentre con entrambi:

ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)

Puoi leggerlo ad alta voce e capire immediatamente cosa fa il codice.

Ricorda che vogliamo scrivere codice facile (= veloce) da capire, non codice veloce da scrivere. Non confondere la velocità con cui puoi stabilire il codice con la velocità con cui completi i progetti.


8
Disaccordo. Questo è solo un punto concettuale. E il codice ha un aspetto migliore secondo me, anche se questa è solo la mia opinione.
ouflak
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.