web-dev-qa-db-fra.com

Indexeurs statiques?

Pourquoi les indexeurs statiques sont-ils interdits en C #? Je ne vois aucune raison pour laquelle ils ne devraient pas être autorisés et, en outre, ils pourraient être très utiles.

Par exemple:

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

Le code ci-dessus gagnerait grandement à un indexeur statique. Cependant, il ne compilera pas car les indexeurs statiques ne sont pas autorisés. Pourquoi cela est-il ainsi?

107
Malfist

La notation de l'indexeur nécessite une référence à this. Comme les méthodes statiques ne font pas référence à une instance particulière de la classe, vous ne pouvez pas utiliser this avec elles et, par conséquent, vous ne pouvez pas utiliser la notation d'indexeur sur les méthodes statiques.

La solution à votre problème consiste à utiliser un motif singleton comme suit:

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

Vous pouvez maintenant appeler Utilities.ConfigurationManager["someKey"] en utilisant la notation d'indexeur.

64
Juliet

Je pense que cela n'a pas été jugé utile. Je pense que c'est une honte aussi - un exemple que j'ai tendance à utiliser est l'encodage, où Encoding.GetEncoding("foo") pourrait être Encoding["Foo"]. Je ne pense pas que cela reviendrait souvent {très}, mais mis à part toute autre chose, il semble un peu incohérent de ne pas être disponible.

Je devrais vérifier, mais je suspect il est déjà disponible dans le langage intermédiaire (IL).

84
Jon Skeet

En guise de solution de contournement, vous pouvez définir un indexeur d'instance sur un objet singleton/statique (par exemple, ConfigurationManager est un singleton, au lieu d'être une classe statique):

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

Avec les nouvelles constructions en C # 6, vous pouvez simplifier le motif singleton avec un corps d’expression de propriété. Par exemple, j’ai utilisé le raccourci suivant qui fonctionne bien avec Code-Lense:

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

Il présente l’avantage supplémentaire de pouvoir être remplacé pour la mise à niveau de code ancien et l’unification de l’accès aux paramètres de votre application.

0
vGHazard

J'étais également dans le besoin (bien, plutôt de Nice-à-avoir) d'un indexeur statique pour stocker des attributs, alors j'ai trouvé une solution de contournement quelque peu gênante:

Dans la classe où vous souhaitez avoir un indexeur statique (ici: Element), créez une sous-classe du même nom + "Dict". Donnez-lui une instruction statique en lecture seule comme instance de ladite sous-classe, puis ajoutez l'indexeur souhaité.

Enfin, ajoutez la classe en tant qu'importation statique (d'où la sous-classe qui n'expose que le champ statique).

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

et vous pouvez ensuite l'utiliser soit en majuscule en tant que Type, soit en tant que dictionnaire:

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

Mais hélas, si on utilisait réellement un objet comme "valeur" -Type, le texte ci-dessous serait toujours plus court (au moins comme déclaration) et fournirait également une conversion immédiate:

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
DW.com