Статические индексаторы? - PullRequest
       44

Статические индексаторы?

109 голосов
/ 30 декабря 2008

Почему статические индексаторы запрещены в C #? Я не вижу причин, почему они не должны быть допущены и, кроме того, они могут быть очень полезны.

Например:

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

Приведенный выше код значительно выиграл бы от статического индексатора. Однако он не скомпилируется, потому что статические индексаторы не разрешены. Почему это так?

Ответы [ 7 ]

87 голосов
/ 30 декабря 2008

Я полагаю, что это считается не очень полезным. Я думаю, что это тоже позор - пример, который я склонен использовать, это Кодировка, где Encoding.GetEncoding("foo") может быть Encoding["Foo"]. Я не думаю, что это будет происходить очень часто, но кроме всего прочего это просто кажется немного непоследовательным, чтобы быть недоступным.

Я должен был бы проверить, но я подозреваю он уже доступен на IL (Intermediate Language).

66 голосов
/ 30 декабря 2008

Для обозначения индексатора требуется ссылка на this. Поскольку статические методы не имеют ссылки на конкретный экземпляр класса, вы не можете использовать this с ними, и, следовательно, вы не можете использовать нотацию индексатора для статических методов.

Решением вашей проблемы является использование шаблона синглтона следующим образом:

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

Теперь вы можете звонить Utilities.ConfigurationManager["someKey"], используя запись индексатора.

7 голосов
/ 30 декабря 2008

В качестве обходного пути вы можете определить индексатор экземпляра для одноэлементного / статического объекта (скажем, что ConfigurationManager является одноэлементным, а не статическим классом):

class ConfigurationManager
{
  //private constructor
  ConfigurationManager() {}
  //singleton instance
  public static ConfigurationManager singleton;
  //indexer
  object this[string name] { ... etc ... }
}
0 голосов
/ 05 марта 2019

Мне также был нужен (ну, больше похож на красивый) статический индексатор для хранения атрибутов, поэтому я нашел несколько неловкий обходной путь:

Внутри класса, в котором вы хотите иметь статический индексатор (здесь: Элемент), создайте подкласс с тем же именем + "Dict". Дайте ему статическую информацию только для чтения в качестве экземпляра указанного подкласса, а затем добавьте нужный индексатор.

Наконец, добавьте класс как статический импорт (следовательно, подкласс должен представлять только статическое поле).

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

и затем вы можете использовать его либо с заглавной буквы как Тип, либо без словаря:

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

Но, увы, если бы кто-то на самом деле использовал объект как тип-значение, то нижеприведенное было бы еще короче (по крайней мере, как объявление), а также обеспечило бы немедленное приведение типов:

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 голосов
/ 28 августа 2018

С более новыми конструкциями в C # 6 вы можете упростить шаблон синглтона с помощью тела выражения свойства. Например, я использовал следующий ярлык, который прекрасно работает с code-lense:

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

Дополнительным преимуществом является возможность поиска и замены для обновления старого кода и унификации доступа к настройкам приложения.

0 голосов
/ 31 января 2018

Причина в том, что довольно трудно понять, что именно вы индексируете с помощью статического индексатора.

Вы говорите, что код выиграет от статического индексатора, но так ли это на самом деле? Все, что нужно сделать, это изменить это:

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

В это:

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

, что никак не улучшает код; он не меньше по размеру строк кода, его не так легко написать благодаря автозаполнению, и он менее понятен, поскольку скрывает тот факт, что вы получаете и устанавливаете то, что вы называете «свойством», и фактически заставляет читателя прочитайте документацию о том, что именно возвращает или устанавливает индексатор, потому что ни в коем случае не очевидно, что это свойство, для которого вы индексируете, в то время как с обоими:

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

Вы можете прочитать его вслух и сразу понять, что делает код.

Помните, что мы хотим писать код, который легко (= быстро) понять, а не код, который быстро пишется. Не путайте скорость, с которой вы можете сложить код, со скоростью, с которой вы выполняете проекты.

0 голосов
/ 27 октября 2013

Это ключевое слово относится к текущему экземпляру класса. Статические функции-члены не имеют указателя this. Это ключевое слово может использоваться для доступа к членам внутри конструкторов, методов экземпляров и методов доступа к экземплярам (извлечено из msdn ). Поскольку он ссылается на экземпляр класса, он вступает в противоречие с природой static, поскольку static не связан с экземпляром класса.

Один из обходных путей заключается в следующем, который позволяет использовать индексатор против частного Словарь, поэтому вам нужно только создать новый экземпляр и получить доступ к статической части.

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

Это позволяет вам пропустить весь доступ к члену класса и просто создать его экземпляр и проиндексировать его.

    new ConfigurationManager()["ItemName"]
...