Почему статические индексаторы запрещены в 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];
}
}
Приведенный выше код значительно выиграет от статического индексатора. Однако он не будет компилироваться, потому что статические индексаторы не разрешены. Почему это так?





В качестве обходного пути вы можете определить индексатор экземпляра для одноэлементного / статического объекта (скажем, что 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? Ему не нужен доступ к данным экземпляра
+1 за комментарий Малфиста. Тот факт, что он использует "this" для индексатора экземпляра, не означает, что они не могли придумать другой синтаксис.
Согласовано. Вы задаетесь вопросом. Вы в основном сказали, что это не разрешено, потому что это не разрешено. -1 потому что вопрос был «а почему нельзя?»
Это не тот ответ, который вы ищете. Двигайтесь дальше.
@ xr280xr +1 За корректное использование "напрашивается вопрос" :) Плюс у меня такая же жалоба.
Я сомневаюсь, что для обозначения индексатора «требуется ссылка на this». Обозначение индексатора может использовать жетонthis, но это вряд ли связано с выражением this, как оно появляется в телах методов экземпляра.
-1, потому что этот ответ предполагает, что текущая нотация является единственно возможной нотацией, если был реализован статический индексатор. Использование this в индексаторе не обязательно, скорее всего, он был выбран выше других ключевых слов, потому что имел наибольший смысл. Для статической реализации может оказаться вполне приемлемым следующий синтаксис: public object static[string value]. Нет необходимости использовать ключевое слово this в статическом контексте.
-1, потому что токен this используется как ключевое слово, а не как ссылка на объект this. И методы расширения вполне могут использовать this в качестве ключевого слова, будучи в то же время статичными.
Каждый раз, когда я пытался использовать C#, я обнаруживал действительно дрянные вещи вроде этого ...
@MarcoMp Если только Джульетта не является человеком, который на самом деле решил не включать это в C#, кажется неуместным отрицать этот ответ, который на самом деле объясняет синтаксическое обоснование, нравится вам это или нет.
@DaveInCaz Потому что это неправильно. Он говорит: «Обозначение индексатора требует ссылки на это». Это неправда, «this» в синтаксисе индексатора - не более чем ключевое слово и никоим образом не ссылается на ссылку на текущий объект; Сам C# противоречит утверждению: методы расширения используют this в качестве ключевого слова в объявлении параметра. Ничто (особенно не перегруженное использование ключевого слова this) не помешало бы реализовать статические индексаторы, это был просто осознанный выбор. См. Также комментарии Джона Скита и einsteinsci для альтернативного синтаксиса или O.R.Mapper для «this».
Я очень признателен тебе, @ Джульетта, ты сэкономила мне время.
Я считаю, что это считалось не очень полезным. Я тоже думаю, что это позор - пример, который я обычно использую, - это кодирование, где Encoding.GetEncoding("foo") может быть Encoding["Foo"]. Я не думаю, что очень будет часто появляться, но, помимо всего прочего, это просто кажется немного непоследовательным из-за того, что он недоступен.
Я должен был бы проверить, но я подозревать он уже доступен на IL (промежуточном языке).
Промежуточный язык - своего рода ассемблер для .NET.
Что привело меня сюда, так это то, что у меня есть собственный класс, который предоставляет словарь общих значений, используемых во всем моем приложении, через статическое свойство. Я надеялся использовать статический индексатор, чтобы сократить доступ от GlobalState.State [KeyName] до GlobalState [KeyName]. Было бы хорошо.
FWIW, изменение instance на static в IL для свойства и метода получения для свойства по умолчанию приводит к тому, что ilasm жалуется на syntax error at token 'static'; Я не очень силен во вмешательстве в дела IL, но это звучит, по крайней мере, как начальное «нет».
Ключевое слово 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
Как и Ответ Джульетты, это не отвечает на вопрос, почему не поддерживаются статические индексаторы. Во-первых, вопрос не ограничивает термин «статический индексатор» до «чего-то, что использует ключевое слово this», а во-вторых, this в синтаксисе public string this[int index], строго говоря, даже не использует указатель this (поскольку он может встречаться в теле методов экземпляра), но просто еще одно использование жетонthis. Синтаксис public static string this[int index] может показаться смотрю несколько нелогичным, но все равно однозначным.
@ O.R.Mapper Это может быть и public static string class[int index].
Я думаю, я сбит с толку. Я подумал: «Ключевое слово this относится к текущему экземпляру класса. Статические функции-члены не имеют указателя this. ' объяснил, что, поскольку нет объекта для ссылки на указатель this, вы не можете использовать ссылку на него. И я также заявил, что msdn был тем, кто использует это определение. общедоступная статическая строка и общедоступная строка никогда не будут перекрываться, насколько мне известно, из-за того, что один обращается к объекту универсального типа, а другой - к объекту экземпляра.
Причина в том, что довольно сложно понять, что именно вы индексируете с помощью статического индексатора.
Вы говорите, что статический индексатор принесет пользу коду, но так ли это на самом деле? Все, что он сделает, это изменит это:
ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)
В это:
ConfigurationManager[name] = value
...
value = ConfigurationManager[name]
что никак не улучшает код; он не меньше на много строк кода, его не легче писать благодаря автозаполнению, и он менее понятен, поскольку скрывает тот факт, что вы получаете и устанавливаете то, что вы называете `` Свойством '', и фактически заставляет читателя прочтите документацию о том, что именно индексатор возвращает или устанавливает, потому что никоим образом не очевидно, что это свойство, для которого вы индексируете, в то время как с обоими:
ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)
Вы можете прочитать это вслух и сразу понять, что делает код.
Помните, что мы хотим писать код, который легко (= быстро) понимать, а не код, который быстро пишется. Не путайте скорость, с которой вы можете написать код, со скоростью, с которой вы завершаете проекты.
Не согласен. Это просто концептуальный момент. И код действительно выглядит лучше, на мой взгляд, хотя это всего лишь мое мнение.
С новыми конструкциями в 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);
Затем мне нужна прямая реализация IEnumerable в статическом классе, поэтому я могу сделать
foreach (var enum in Enum):)