Statische Indexer?

119

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.

Beispielsweise:

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 stark von einem statischen Indexer profitieren. Es wird jedoch nicht kompiliert, da statische Indexer nicht zulässig sind. Warum ist das so?

Malfist
quelle
Als nächstes möchte ich eine direkte IEnumerable-Implementierung für eine statische Klasse, damit ich dies tun kann foreach (var enum in Enum):)
nawfal

Antworten:

72

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

Die Lösung für Ihr Problem 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 Indexer-Notation aufrufen .

Julia
quelle
110
Aber warum muss der Indexer "dies" verwenden? Es muss nicht auf
Instanzdaten
80
+1 für Malfists Kommentar. Nur weil "this" für einen Instanzindexer verwendet wird, heißt das nicht, dass sie keine andere Syntax finden konnten.
Jon Skeet
40
Einverstanden. Sie stellen die Frage. Sie haben im Grunde gesagt, der Grund, warum es nicht erlaubt ist, ist, dass es nicht erlaubt ist. -1 weil die Frage war "warum ist es nicht erlaubt?"
xr280xr
15
@ xr280xr +1 Für die korrekte Verwendung von "die Frage stellen" :) Außerdem habe ich die gleiche Beschwerde.
RedFilter
14
-1, da diese Antwort davon ausgeht, dass die aktuelle Notation die einzig mögliche Notation ist, wenn ein statischer Indexer implementiert wurde. Die Verwendung thisin einem Indexer ist nicht unbedingt erforderlich. Sie wurde wahrscheinlich vor anderen Schlüsselwörtern ausgewählt, da dies am sinnvollsten war. Für eine statische Implementierung kann die folgende Syntax durchaus sinnvoll sein : public object static[string value]. Das Schlüsselwort muss nicht thisin einem statischen Kontext verwendet werden.
einsteinsci
91

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

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

Jon Skeet
quelle
6
Intermediate Language - eine Art Assemblersprache für .NET.
Jon Skeet
14
Was mich hierher gebracht hat, ist, dass ich eine benutzerdefinierte Klasse habe, die ein Wörterbuch allgemeiner Werte verfügbar macht, die in meiner Anwendung über eine statische Eigenschaft verwendet werden. Ich hatte gehofft, einen statischen Indexer verwenden zu können, um den Zugriff von GlobalState.State [KeyName] auf GlobalState [KeyName] zu verkürzen. Wäre nett gewesen.
xr280xr
1
FWIW, Ändern , instanceum staticin der IL für eine Eigenschaft und Getter - Verfahren auf eine Standardeigenschaft Ergebnisse in ILASM Beschweren syntax error at token 'static'; Ich kann mich nicht gut in die Angelegenheiten von IL einmischen, aber das klingt zumindest nach einem anfänglichen Nein.
Erstaunlich
8

Als Workaround können Sie einen Instanzindexer für ein Singleton / statisches Objekt definieren (sagen wir, ConfigurationManager ist ein Singleton, anstatt eine statische Klasse zu sein):

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

Ich brauchte auch einen statischen Indexer zum Speichern von Attributen (also eher nett zu haben), also fand ich eine etwas umständliche Problemumgehung:

Erstellen Sie innerhalb der Klasse, für die Sie einen statischen Indexer haben möchten (hier: Element), eine gleichnamige Unterklasse + "Dict". Geben Sie eine schreibgeschützte statische Instanz dieser Unterklasse ein und fügen Sie dann den gewünschten Indexer hinzu.

Fügen Sie zuletzt 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 als Typ großgeschrieben oder ohne als Wörterbuch verwenden:

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

Aber leider, wenn man ein Objekt tatsächlich als "Wert" -Typ verwenden würde, wäre das Folgende noch kürzer (zumindest als Deklaration) und würde auch eine sofortige Typumwandlung ermöglichen:

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
quelle
0

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

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

Es hat den zusätzlichen Vorteil, dass es für das Aktualisieren von älterem Code und das Vereinheitlichen des Zugriffs auf die Anwendungseinstellungen gefunden und ersetzt werden kann.

vGHazard
quelle
-2

Das Schlüsselwort this bezieht sich auf die aktuelle Instanz der Klasse. Statische Elementfunktionen haben keinen this-Zeiger. Mit diesem Schlüsselwort kann auf Mitglieder innerhalb von Konstruktoren, Instanzmethoden und Instanzzugriffsgruppen zugegriffen werden (abgerufen von msdn ). Da dies auf eine Instanz der Klasse verweist, widerspricht es der Natur von static, da static keiner Instanz der Klasse zugeordnet ist.

Eine Problemumgehung ist die folgende, mit der Sie den Indexer für ein privates Wörterbuch verwenden können, sodass Sie nur eine neue Instanz erstellen und auf den statischen Teil zugreifen müssen.

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

Auf diese Weise können Sie den gesamten Zugriff auf ein Mitglied der Klasse überspringen und einfach eine Instanz davon erstellen und indizieren.

    new ConfigurationManager()["ItemName"]
Lamorach
quelle
4
Es ist eine interessante Problemumgehung, aber 1) es führt zu Nebenwirkungen (Erstellung eines leeren Instanzobjekts), die in einigen Umgebungen zu Speicherdruck und Fragmentierung führen können. 2) Die zusätzlichen Zeichen, die durch verschwendet wurden, new ()könnten für den Qualifikationsnamen eines Singletons verwendet worden sein stattdessen wie.Current
Lawrence Ward
1
Genau wie Julias Antwort beantwortet dies nicht die Frage, warum statische Indexer nicht unterstützt werden. Erstens beschränkt die Frage den Begriff "statischer Indexer" nicht auf "etwas, das das thisSchlüsselwort verwendet", und zweitens ist thisin der Syntax public string this[int index]streng genommen nicht einmal die Verwendung eines thisZeigers (wie er im Hauptteil der Instanzmethoden vorkommen kann). , aber nur eine andere Verwendung des Tokens this . Die Syntax siehtpublic static string this[int index] vielleicht etwas eingängig aus, ist aber dennoch eindeutig.
ODER Mapper
2
@ORMapper Es könnte genauso gut sein public static string class[int index].
Jim Balter
Ich glaube, ich bin verwirrt. Ich dachte: 'Dieses Schlüsselwort bezieht sich auf die aktuelle Instanz der Klasse. Statische Elementfunktionen haben keinen this-Zeiger. ' erklärte, dass es keinen Verweis auf diesen Zeiger gibt, auf den verwiesen werden kann, und dass Sie den Verweis darauf nicht verwenden können. Und ich sagte auch, dass msdn derjenige ist, der diese Definition verwendet. öffentliche statische Zeichenfolge vs. öffentliche Zeichenfolge überschneiden sich meines Wissens nie, da einer auf das generische Typobjekt zugreift, während der andere auf ein Instanzobjekt zugreift.
Lamorach
-2

Der Grund dafür ist, dass es ziemlich schwer zu verstehen ist, was genau Sie mit einem statischen Indexer indizieren.

Sie sagen, dass der Code von einem statischen Indexer profitieren würde, aber würde es wirklich? Alles was es tun würde, ist dies zu ändern:

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

Das mögen:

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

was den Code in keiner Weise besser macht; Es ist nicht um viele Codezeilen kleiner, es ist dank der automatischen Vervollständigung nicht einfacher zu schreiben und es ist weniger klar, da es die Tatsache verbirgt, dass Sie etwas erhalten und festlegen, das Sie "Eigenschaft" nennen, und es den Leser tatsächlich dazu zwingt Lesen Sie die Dokumentation darüber, was genau der Indexer zurückgibt oder festlegt, da es keineswegs offensichtlich ist, dass es sich um eine Eigenschaft handelt, für die Sie indizieren, während Sie mit beiden:

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

Sie können es laut vorlesen und sofort verstehen, was der Code tut.

Denken Sie daran, dass wir Code schreiben möchten, der leicht (= schnell) zu verstehen ist, nicht Code, der schnell zu schreiben ist. Verwechseln Sie nicht die Geschwindigkeit, mit der Sie den Code festlegen können, mit der Geschwindigkeit, mit der Sie Projekte abschließen.

Kuchen
quelle
8
Nicht zustimmen. Das ist nur ein konzeptioneller Punkt. Und der Code sieht meiner Meinung nach besser aus, obwohl das nur meine Meinung ist.
Ouflak