Indexeurs statiques?

119

Pourquoi les indexeurs statiques sont-ils interdits en C #? Je ne vois aucune raison pour laquelle ils ne devraient pas être autorisés et ils pourraient en outre ê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 bénéficierait grandement d'un indexeur statique. Cependant, il ne se compilera pas car les indexeurs statiques ne sont pas autorisés. Pourquoi cela est-il ainsi?

Malfist
la source
Ensuite, je veux une implémentation directe de IEnumerable sur une classe statique, donc je peux le faire foreach (var enum in Enum):)
nawfal

Réponses:

72

La notation de l'indexeur nécessite une référence à this. Étant donné que les méthodes statiques n'ont pas de référence à une instance particulière de la classe, vous ne pouvez pas les utiliser thisavec elles et, par conséquent, vous ne pouvez pas utiliser la notation indexeur sur les méthodes statiques.

La solution à votre problème consiste à utiliser un modèle 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 en Utilities.ConfigurationManager["someKey"]utilisant la notation indexeur.

Juliette
la source
110
Mais pourquoi l'indexeur doit-il utiliser «ceci»? Il n'a pas besoin d'accéder aux données d'instance
Malfist
80
+1 pour le commentaire de Malfist. Ce n'est pas parce qu'il utilise "this" pour un indexeur d'instance qu'ils ne peuvent pas trouver une autre syntaxe.
Jon Skeet
40
D'accord. Vous mendiez la question. Vous avez essentiellement dit que la raison pour laquelle ce n'est pas autorisé est que ce n'est pas autorisé. -1 parce que la question était "pourquoi n'est-il pas autorisé?"
xr280xr
15
@ xr280xr +1 Pour une utilisation correcte de "mendier la question" :) De plus, j'ai la même plainte.
RedFilter
14
-1 car cette réponse suppose que la notation actuelle est la seule notation possible si un indexeur statique était implémenté. L'utilisation de thisdans un indexeur n'est pas nécessairement obligatoire, elle a probablement été choisie au-dessus des autres mots-clés car elle était la plus logique. Pour une implémentation statique, la syntaxe suivante peut être tout à fait viable: public object static[string value]. Pas besoin d'utiliser le mot-clé thisdans un contexte statique.
einsteinsci
91

Je pense que cela n'a pas été jugé très utile. Je pense que c'est dommage 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 très souvent, mais à part toute autre chose, il semble juste un peu incohérent de ne pas être disponible.

Je devrais vérifier, mais je soupçonne que c'est déjà disponible en IL (Intermediate Language).

Jon Skeet
la source
6
Intermediate Language - sorte de langage d'assemblage pour .NET.
Jon Skeet
15
Ce qui m'a amené ici, c'est que j'ai une classe personnalisée qui expose un dictionnaire de valeurs communes utilisées dans toute mon application via une propriété statique. J'espérais utiliser un indexeur statique pour raccourcir l'accès de GlobalState.State [KeyName] à GlobalState [KeyName]. Ça aurait été bien.
xr280xr
1
FWIW, le passage instanceà staticdans l'IL pour une propriété et une méthode getter sur une propriété par défaut entraîne une plainte d'ilasm syntax error at token 'static'; Je ne suis pas doué pour me mêler des affaires de l'IL, mais cela ressemble au moins à un non initial.
Amazingant
8

Pour contourner le problème, vous pouvez définir un indexeur d'instance sur un objet singleton / statique (disons que 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 ... }
}
ChrisW
la source
1

J'avais également besoin (enfin, plutôt comme gentil à avoir) d'un indexeur statique pour stocker les attributs, j'ai donc trouvé une solution de contournement quelque peu délicate:

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

Enfin, ajoutez la classe en tant qu'import statique (d'où la sous-classe pour exposer uniquement 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 en majuscule comme Type ou sans comme dictionnaire:

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

Mais hélas, si l'on utilisait réellement un objet comme "valeur" -Type, alors le ci-dessous serait encore plus court (au moins comme déclaration), et fournirait également un typage immédiat:

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);
DW.com
la source
0

Avec les nouvelles constructions en C # 6, vous pouvez simplifier le modèle de 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 trouvé et remplacé pour mettre à niveau un code plus ancien et unifier l'accès aux paramètres de votre application.

vGHazard
la source
-2

Le mot-clé this fait référence à l'instance actuelle de la classe. Les fonctions membres statiques n'ont pas de pointeur this. Le mot clé this peut être utilisé pour accéder aux membres depuis des constructeurs, des méthodes d'instance et des accesseurs d'instance (récupéré à partir de msdn ). Puisque cela fait référence à une instance de la classe, il entre en conflit avec la nature de static, puisque static n'est pas associé à une instance de la classe.

Une solution de contournement serait la suivante qui vous permet d'utiliser l'indexeur sur un dictionnaire privé afin que vous n'ayez qu'à créer une nouvelle instance et vous accédez à la partie statique.

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

Cela vous permet d'ignorer tout l'accès à un membre de la classe et de simplement créer une instance de celui-ci et de l'indexer.

    new ConfigurationManager()["ItemName"]
Lamorach
la source
4
c'est une solution de contournement intéressante, mais 1) elle introduit des effets secondaires (création d'un objet d'instance vide) qui pourraient entraîner une pression de mémoire et une fragmentation dans certains environnements, 2) les caractères supplémentaires gaspillés par new ()pourraient avoir été utilisés pour le nom du qualificatif d'un singleton à la place, comme.Current
Lawrence Ward
1
Tout comme la réponse de Juliet , cela ne répond pas à la question de savoir pourquoi les indexeurs statiques ne sont pas pris en charge. Premièrement, la question ne limite pas le terme "indexeur statique" à "quelque chose qui utilise le thismot - clé", et deuxièmement, thisdans la syntaxe, ce public string this[int index]n'est même pas à proprement parler une utilisation d'un thispointeur (comme cela peut se produire dans le corps des méthodes d'instance) , mais juste une autre utilisation du jeton this . La syntaxe public static string this[int index]pourrait sembler un peu contre-intuitive, mais elle serait toujours sans ambiguïté.
OR Mapper
2
@ORMapper Cela pourrait tout aussi bien l'être public static string class[int index].
Jim Balter
Je suppose que je suis confus, j'ai pensé 'Le mot-clé this fait référence à l'instance actuelle de la classe. Les fonctions membres statiques n'ont pas de pointeur this. ' expliquait que puisqu'il n'y a pas d'objet pour le pointeur this à référencer, vous ne pouvez pas utiliser la référence à celui-ci. Et j'ai également déclaré que msdn était celui qui utilise cette définition. La chaîne publique statique et la chaîne publique ne se chevaucheront jamais à ma connaissance car l'un accède à l'objet de type générique, tandis que l'autre accède à un objet d'instance.
lamorach le
-2

La raison en est qu'il est assez difficile de comprendre exactement ce que vous indexez avec un indexeur statique.

Vous dites que le code bénéficierait d'un indexeur statique, mais le serait-il vraiment? Tout ce qu'il ferait serait de changer cela:

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

Dans ceci:

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

ce qui n'améliore en rien le code; il n'est pas plus petit par de nombreuses lignes de code, ce n'est pas plus facile à écrire grâce à la saisie semi-automatique et c'est moins clair, car cela cache le fait que vous obtenez et définissez quelque chose que vous appelez `` Propriété '' et cela oblige en fait le lecteur à allez lire la documentation sur ce que l'indexeur renvoie ou définit exactement, car il n'est en aucun cas évident qu'il s'agit d'une propriété pour laquelle vous indexez, alors qu'avec les deux:

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

Vous pouvez le lire à haute voix et comprendre immédiatement ce que fait le code.

N'oubliez pas que nous voulons écrire du code facile (= rapide) à comprendre, pas du code rapide à écrire. Ne confondez pas la vitesse à laquelle vous pouvez établir le code avec la vitesse à laquelle vous terminez les projets.

tarte
la source
8
Être en désaccord. C'est simplement un point conceptuel. Et le code est meilleur à mon avis, bien que ce ne soit que mon avis.
ouflak