it-swarm.com.de

Statische Indexer

Warum sind statische Indexer in C # nicht zulässig? Ich sehe keinen Grund, warum sie nicht erlaubt sein sollten, und außerdem könnten sie sehr nützlich sein.

Zum Beispiel:

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

Der obige Code würde von einem statischen Indexer stark profitieren. Es wird jedoch nicht kompiliert, da statische Indexer nicht zulässig sind. Warum ist das so?

107
Malfist

Die Indexer-Notation erfordert einen Verweis auf this. Da statische Methoden keinen Verweis auf eine bestimmte Instanz der Klasse haben, können Sie nicht this verwenden, und Sie können daher keine Indexer-Notation für statische Methoden verwenden.

Die Lösung Ihres Problems besteht darin, ein Singleton-Muster wie folgt zu verwenden:

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

Jetzt können Sie Utilities.ConfigurationManager["someKey"] mit der Indexer-Notation aufrufen.

64
Juliet

Ich glaube, es wurde als nicht besonders nützlich angesehen. Ich finde es auch schade - ein Beispiel, das ich gerne benutze, ist Encoding, wobei Encoding.GetEncoding("foo")Encoding["Foo"] sein könnte. Ich glaube nicht, dass es sehr oft auftaucht, aber abgesehen von allem anderen fühlt es sich ein bisschen inkonsistent an, nicht verfügbar zu sein.

Ich müsste das überprüfen, aber ich Verdächtiger es ist bereits in IL (Intermediate Language) verfügbar.

87
Jon Skeet

Um dieses Problem zu umgehen, können Sie einen Instanz-Indexer für ein Singleton/statisches Objekt definieren (sagen Sie, dass ConfigurationManager ein Singleton ist, anstatt eine statische Klasse zu sein):

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

Mit den neueren Konstrukten in C # 6 können Sie das Singleton-Muster mit einem Eigenschaftenausdruckskörper vereinfachen. Zum Beispiel habe ich die folgende Abkürzung verwendet, die gut mit Code-Lense funktioniert:

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

Es bietet den zusätzlichen Vorteil, dass es für das Aktualisieren älterer Codes und für die Vereinheitlichung des Zugriffs auf die Anwendungseinstellungen durch Suchfunktion ersetzt werden kann.

0
vGHazard

Ich brauchte auch einen statischen Indexer, um Attribute zu speichern, und fand eine etwas umständliche Lösung:

Erstellen Sie innerhalb der Klasse, die einen statischen Indexer haben soll (hier: Element), eine Unterklasse mit dem gleichen Namen + "Dict". Geben Sie eine readonly static-Instanz dieser Unterklasse an und fügen Sie dann den gewünschten Indexer hinzu.

Zuletzt fügen Sie die Klasse als statischen Import hinzu (daher die Unterklasse, um nur das statische Feld verfügbar zu machen).

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

und dann können Sie es entweder in Großbuchstaben als Typ oder ohne Wörterbuch verwenden:

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

Aber wenn man tatsächlich ein Objekt als "value" -Type verwenden würde, dann wäre das Folgende noch kürzer (zumindest als Deklaration) und würde auch eine sofortige Typumwandlung liefern:

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