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

Почему статические индексаторы запрещены в 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 04.07.2016 14:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
124
1
44 933
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

class ConfigurationManager
{
  //private constructor
  ConfigurationManager() {}
  //singleton instance
  public static ConfigurationManager singleton;
  //indexer
  object this[string name] { ... etc ... }
}
Ответ принят как подходящий

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

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

Malfist 30.12.2008 22:49

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

Jon Skeet 30.12.2008 23:19

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

xr280xr 10.11.2012 00:39

Это не тот ответ, который вы ищете. Двигайтесь дальше.

kjbartel 17.09.2014 04:21

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

D'Arcy Rittich 17.12.2014 22:04

Я сомневаюсь, что для обозначения индексатора «требуется ссылка на this». Обозначение индексатора может использовать жетонthis, но это вряд ли связано с выражением this, как оно появляется в телах методов экземпляра.

O. R. Mapper 29.12.2014 17:28

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

einsteinsci 26.05.2015 18:20

-1, потому что токен this используется как ключевое слово, а не как ссылка на объект this. И методы расширения вполне могут использовать this в качестве ключевого слова, будучи в то же время статичными.

Marco Mp 29.01.2016 13:18

Каждый раз, когда я пытался использовать C#, я обнаруживал действительно дрянные вещи вроде этого ...

Eugene 31.05.2018 23:02

@MarcoMp Если только Джульетта не является человеком, который на самом деле решил не включать это в C#, кажется неуместным отрицать этот ответ, который на самом деле объясняет синтаксическое обоснование, нравится вам это или нет.

StayOnTarget 11.06.2019 18:55

@DaveInCaz Потому что это неправильно. Он говорит: «Обозначение индексатора требует ссылки на это». Это неправда, «this» в синтаксисе индексатора - не более чем ключевое слово и никоим образом не ссылается на ссылку на текущий объект; Сам C# противоречит утверждению: методы расширения используют this в качестве ключевого слова в объявлении параметра. Ничто (особенно не перегруженное использование ключевого слова this) не помешало бы реализовать статические индексаторы, это был просто осознанный выбор. См. Также комментарии Джона Скита и einsteinsci для альтернативного синтаксиса или O.R.Mapper для «this».

Marco Mp 12.06.2019 19:20

Я очень признателен тебе, @ Джульетта, ты сэкономила мне время.

Mohsen Zahedi 14.10.2020 09:22

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

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

Промежуточный язык - своего рода ассемблер для .NET.

Jon Skeet 30.12.2008 23:36

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

xr280xr 10.11.2012 00:45

FWIW, изменение instance на static в IL для свойства и метода получения для свойства по умолчанию приводит к тому, что ilasm жалуется на syntax error at token 'static'; Я не очень силен во вмешательстве в дела IL, но это звучит, по крайней мере, как начальное «нет».

Amazingant 24.03.2015 22:06

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

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

    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"]

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

Lawrence Ward 10.03.2014 17:22

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

O. R. Mapper 29.12.2014 17:27

@ O.R.Mapper Это может быть и public static string class[int index].

Jim Balter 28.07.2015 23:57

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

lamorach 29.07.2015 16:37

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

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

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

В это:

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

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

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

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

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

Не согласен. Это просто концептуальный момент. И код действительно выглядит лучше, на мой взгляд, хотя это всего лишь мое мнение.

ouflak 02.02.2018 18:08

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

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

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

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

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

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

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

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