Domanda

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

Ad 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 sopra riportato trarrebbe grandi vantaggi da un indicizzatore statico. Tuttavia non verrà compilato perché non sono consentiti indicizzatori statici. Perché è così?

È stato utile?

Soluzione

La notazione dell'indicizzatore richiede un riferimento a this . Poiché i metodi statici non hanno un riferimento a una particolare istanza della classe, non puoi usare this con loro, e di conseguenza non puoi usare la notazione dell'indicizzatore su metodi statici.

La soluzione al tuo problema sta usando uno schema 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.

Altri suggerimenti

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

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

Come soluzione alternativa, puoi definire un indicizzatore di istanza su un oggetto singleton / statico (supponi che ConfigurationManager sia un singleton, anziché essere una classe statica):

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

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

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

Ha l'ulteriore vantaggio di essere trova-sostituibile per aggiornare il codice precedente e unificare l'accesso alle impostazioni dell'applicazione.

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

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

Infine, aggiungi la classe come importazione statica (quindi 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 in maiuscolo come Tipo o senza come dizionario:

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

Ma purtroppo, se si dovesse effettivamente usare l'oggetto come tipo "value", allora il seguito sarebbe ancora più breve (almeno come dichiarazione), e fornirebbe anche il 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);

La parola chiave this si riferisce all'istanza corrente della classe. Le funzioni membro statiche non hanno questo puntatore. Questa parola chiave può essere utilizzata per accedere ai membri da costruttori, metodi di istanza e accessori di istanza. (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 sarebbe la seguente che consente di utilizzare l'indicizzatore contro un privato Dizionario 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ò ti consente di saltare l'intero accesso a un membro della classe e crearne un'istanza e indicizzarla.

    new ConfigurationManager()["ItemName"]

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

Dici che il codice trarrebbe beneficio da un indicizzatore statico, ma lo farebbe davvero? Tutto ciò che farebbe sarebbe 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, in quanto nasconde il fatto che stai ottenendo e impostando qualcosa che chiami "Proprietà" e costringe il lettore a vai a leggere la documentazione su cosa restituisce o imposta esattamente l'indicizzatore, perché non è affatto ovvio che si tratta di una proprietà per cui stai indicizzando, mentre con entrambi:

<*>

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

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top