Статичні показники?


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

Вищевказаний код матиме велику користь від статичного індексатора. Однак він не компілюється, оскільки статичні індекси не дозволені. Чому це так?


Далі я хочу прямої foreach (var enum in Enum)
IE численні

Відповіді:


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
Але чому індексатору доводиться використовувати "це"? Він не має доступу до даних екземплярів
Malfist

80
+1 за коментар Malfist Тільки тому, що він використовує "це" для індексатора екземпляра, це не означає, що вони не могли придумати інший синтаксис.
Джон Скіт

40
Домовились. Ви благаєте питання. Ви в основному сказали, що причина цього заборонена - це не дозволено. -1 тому, що питання було "чому це не дозволено?"
xr280xr

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

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

91

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

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


6
Intermediate Language - мова збірки для .NET.
Джон Скіт

15
Що мене сюди привело, це те, що у мене є власний клас, який розкриває словник загальних значень, який використовується у моїй програмі через статичну властивість. Я сподівався використати статичний індексатор, щоб скоротити доступ з GlobalState.State [KeyName] до GlobalState [KeyName]. Було б добре.
xr280xr

1
FWIW, зміна instanceна staticIL для методу властивості та getter на властивість за замовчуванням призводить до ілазму syntax error at token 'static'; Я не чудово втручаюся в справи Іллі, але це звучить як принаймні початковий ні.
Дивовижний

8

Як обхід, ви можете визначити індексатор екземпляра для одиночного / статичного об'єкта (скажімо, що ConfigurationManager є однотонним, а не статичним класом):

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

1

Я також потребував (ну, більше схоже на приємне мати) статичного індексатора для зберігання атрибутів, тому я з'ясував дещо незручне рішення:

У межах класу, який ви хочете мати статичний індексатор (тут: 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;

Але на жаль, якби насправді використовувати об’єкт як "value" -тип, то наведене нижче все ще буде коротшим (принаймні як декларація), а також забезпечить негайний Typecasting:

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 ви можете спростити однотонний візерунок з тілом вираження властивості. Наприклад, я використав наступний ярлик, який добре працює з кодовим об'єктивом:

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

Це має додаткову перевагу - знайти-замінити для оновлення старого коду та уніфікації доступу до налаштувань програми.


-2

Це ключове слово стосується поточного екземпляра класу. Статичні функції членів не мають цього покажчика. Це ключове слово може бути використано для членів доступу всередині конструкторів, методів екземпляра і екземпляр аксессор. (Отримано з MSDN ). Оскільки цей посилання на екземпляр класу, він суперечить природі статики, оскільки статичний не асоціюється з екземпляром класу.

Одним із способів вирішити таке рішення, яке дозволяє використовувати індексатор проти приватного словника, тому вам потрібно створити лише новий екземпляр і отримати доступ до статичної частини.

    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
Lawrence Ward

1
Так само, як і відповідь Джульєтти , це не відповідає на питання, чому статичні індексатори не підтримуються. По-перше, питання не обмежує термін "статичний індекс" на "те, що використовує thisключове слово", по-друге, thisв синтаксисі public string this[int index]суворо кажучи навіть не використання thisпокажчика (як це може траплятися в тілі методів екземплярів) , але просто чергове використання маркера this . Синтаксис public static string this[int index]може виглядати дещо протизаконним, але все одно буде однозначним.
АБО Mapper

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

Я думаю, що я розгублений, я подумав: "Це ключове слово стосується поточного примірника класу. Статичні функції членів не мають цього покажчика. ' пояснював, що оскільки для цього вказівника немає об'єкта для посилання, ви не можете використовувати посилання на нього. І я також заявив, що msdn був тим, хто використовує це визначення. public static string vs. public string ніколи не перекриватиметься моїми знаннями через те, що один отримує доступ до об'єкта загального типу, а інший буде отримувати доступ до об'єкта екземпляра.
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.