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


119

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

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


Затем мне нужна прямая реализация IEnumerable в статическом классе, поэтому я могу это сделать foreach (var enum in Enum):)
nawfal

Ответы:


72

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


110
Но почему индексатор должен использовать this? Ему не
нужен

80
+1 за комментарий Малфиста. Тот факт, что он использует "this" для индексатора экземпляра, не означает, что они не могли придумать другой синтаксис.
Джон Скит,

40
Согласовано. Вы задаетесь вопросом. Вы в основном сказали, что это не разрешено, потому что это не разрешено. -1 потому что вопрос был «почему нельзя?»
xr280xr 09

15
@ xr280xr +1 За корректное использование "напрашивается вопрос" :) Плюс у меня такая же жалоба.
RedFilter

14
-1, потому что этот ответ предполагает, что текущая нотация является единственно возможной нотацией, если был реализован статический индексатор. Использование thisв индексаторе не обязательно, скорее всего, оно было выбрано над другими ключевыми словами, поскольку имело наибольший смысл. Для статической реализации, следующий синтаксис может быть вполне жизнеспособным: public object static[string value]. Нет необходимости использовать ключевое слово thisв статическом контексте.
einsteinsci

91

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

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


6
Intermediate Language - своего рода ассемблер для .NET.
Джон Скит,

15
Что привело меня сюда, так это то, что у меня есть собственный класс, который предоставляет словарь общих значений, используемых во всем моем приложении, через статическое свойство. Я надеялся использовать статический индексатор, чтобы сократить доступ от GlobalState.State [KeyName] до GlobalState [KeyName]. Было бы хорошо.
xr280xr 09

1
FWIW, изменение instanceк staticв IL для метода собственности и газопоглощающего по результатам свойства по умолчанию в ILASM жалуясь syntax error at token 'static'; Я не очень хорош во вмешательстве в дела IL, но это звучит как минимум для начала.
Amazingant,

8

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

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

1

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

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

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

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

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

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


-2

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

4
это интересный обходной путь, но 1) он вводит побочные эффекты (создание пустого объекта-экземпляра), которые могут привести к нехватке памяти и фрагментации в некоторых средах, 2) лишние символы, потраченные впустую, new ()могли быть использованы для имени квалификатора синглтона вместо этого вроде.Current
Лоуренс Уорд

1
Как и ответ Джульетты , это не отвечает на вопрос, почему не поддерживаются статические индексаторы. Во-первых, вопрос не ограничивает термин «статический индексатор» thisсловом « что-то, что использует ключевое слово», а во-вторых, thisв синтаксисе public string this[int index], строго говоря, даже не используется thisуказатель (как это может происходить в теле методов экземпляра) , но это просто еще одно использование токена this . Синтаксис public static string this[int index]может показаться несколько нелогичным, но все равно однозначным.
OR Mapper

2
@ORMapper Так могло и быть public static string class[int index].
Джим Балтер

Думаю, я сбит с толку. Я подумал: «Ключевое слово this относится к текущему экземпляру класса. Статические функции-члены не имеют указателя this. ' объяснил, что, поскольку нет объекта для ссылки на указатель this, вы не можете использовать ссылку на него. И я также заявил, что это определение использует msdn. общедоступная статическая строка и общедоступная строка никогда не будут перекрываться, насколько мне известно, из-за того, что один обращается к объекту общего типа, а другой - к объекту экземпляра.
lamorach

-2

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

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

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

В это:

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

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

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

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

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


8
Не согласен. Это просто концептуальный момент. И код действительно выглядит лучше, на мой взгляд, хотя это всего лишь мое мнение.
ouflak
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.