Вопрос

Почему статические индексаторы запрещены в 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];
        }
    }

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

Это было полезно?

Решение

Для обозначения индексатора требуется ссылка на 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"] используя обозначение индексатора.

Другие советы

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

Я должен был бы проверить, но я подозреваемый он уже доступен на IL (промежуточном языке).

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

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

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

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

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

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

Внутри класса вы хотите иметь статический индексатор (здесь:Элемент), создайте подкласс с тем же именем + "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;
        }
    }
}

и тогда вы можете использовать его либо с заглавной буквы как Type, либо без нее как dictionary:

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

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

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

Ключевое слово this ссылается на текущий экземпляр класса.Статические функции-члены не имеют указателя this.Ключевое слово 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"]

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

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

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

В это:

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top