Статические индексы?
Почему статические индексаторы запрещены в С#? Я не вижу причин, почему они не должны быть допущены и, кроме того, они могут быть очень полезными.
Например:
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];
}
}
Приведенный выше код очень выиграл бы от статического индексатора. Однако он не скомпилируется, потому что статические индексаторы не разрешены. Почему это так?
Ответы
Ответ 1
Обозначение индексатора требует ссылки на 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"]
используя нотацию индексатора.
Ответ 2
Я считаю, что это считается не очень полезным. Я думаю, что это тоже позор - пример, который я склонен использовать, это Encoding, где Encoding.GetEncoding("foo")
может быть Encoding["Foo"]
. Я не думаю, что это будет происходить очень часто, но кроме всего прочего это просто кажется немного непоследовательным, чтобы не быть доступным.
Я должен был бы проверить, но я подозреваю, что это доступно на IL (Промежуточный язык) уже.
Ответ 3
Как обход, вы можете определить индексный указатель на одноэлементном/статическом объекте (скажем, что ConfigurationManager является одиночным, а не статическим классом):
class ConfigurationManager
{
//private constructor
ConfigurationManager() {}
//singleton instance
public static ConfigurationManager singleton;
//indexer
object this[string name] { ... etc ... }
}
Ответ 4
С более новыми конструкциями в С# 6 вы можете упростить шаблон синглтона с помощью тела выражения свойства. Например, я использовал следующий ярлык, который хорошо работает с code-lense:
public static class Config
{
public static NameValueCollection Get => ConfigurationManager.AppSettings;
}
Дополнительным преимуществом является возможность поиска и замены для обновления старого кода и унификации доступа к настройкам приложения.
Ответ 5
Причина в том, что довольно сложно понять, что именно вы индексируете с помощью статического индексатора.
Вы говорите, что код выиграет от статического индексатора, но так ли это на самом деле? Все, что нужно сделать, это изменить это:
ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)
В это:
ConfigurationManager[name] = value
...
value = ConfigurationManager[name]
что никак не делает код лучше; он не меньше по количеству строк кода, его не так просто написать благодаря автозаполнению, и он менее понятен, поскольку скрывает тот факт, что вы получаете и устанавливаете то, что вы называете "свойством", и фактически заставляет читателя прочитайте документацию о том, что именно возвращает или устанавливает индексатор, потому что ни в коем случае не очевидно, что это свойство, для которого вы индексируете, в то время как с обоими:
ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)
Вы можете прочитать это вслух и сразу понять, что делает код.
Помните, что мы хотим писать код, который легко (= быстро) понять, а не код, который быстро пишется. Не путайте скорость, с которой вы можете сложить код, со скоростью, с которой вы завершаете проекты.
Ответ 6
Это ключевое слово относится к текущему экземпляру класса. У статических функций-членов нет указателя. Это ключевое слово можно использовать для доступа к элементам изнутри конструкторов, методов экземпляров и экземпляров экземпляров. (Извлечено из 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"]