C# 6 авто-свойства - читать один раз или каждый раз?
Я следую шаблону, когда устанавливаю определенные свойства, проверяя, является ли соответствующее поле пустым, возвращая поле, если нет, и устанавливая его, если так. Я часто использую это для чтения настроек конфигурации, например, чтобы настройки читались лениво и чтобы они читались только один раз. Вот пример:
private string DatabaseId
{
get
{
if (string.IsNullOrEmpty(databaseId))
{
databaseId = CloudConfigurationManager.GetSetting("database");
}
return databaseId;
}
}
Я начал использовать инициализацию autoperperty C# 6, поскольку она действительно очищает и делает мой код более кратким. Я хотел бы сделать что-то вроде этого:
private string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");
Но я не уверен, как компилятор интерпретирует это в этом случае. Будет ли это иметь тот же эффект, что и мой первый блок кода, однажды установив (автоматически реализованное) поле, а затем прочитав его? Или это будет называть CloudConfigurationManager
каждый раз, когда я получаю DatabaseId
?
5 ответов
Что вы показываете:
private string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");
"Инициализатор авто-свойств", ключевое слово "инициализатор", из блогов MSDN: C#: новый и улучшенный C# 6.0:
Инициализатор авто-свойств позволяет присваивать свойства непосредственно в пределах их объявления. Для свойств, доступных только для чтения, он заботится обо всей церемонии, необходимой для обеспечения неизменности свойства.
Инициализаторы запускаются один раз для экземпляра (или один раз для типа для статических членов). См. Спецификация языка C#, 10.4.5 Инициализаторы переменных:
Для полей экземпляров инициализаторы переменных соответствуют операторам присваивания, которые выполняются при создании экземпляра класса.
Таким образом, этот код компилируется примерно так:
public class ContainingClass
{
private readonly string _databaseId;
public string DatabaseId { get { return _databaseId; } }
public ContainingClass()
{
_databaseId = CloudConfigurationManager.GetSetting("database");
}
}
Для статических переменных этот вид выглядит одинаково:
private static string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");
Компилируется в более или менее:
public class ContainingClass
{
private static readonly string _databaseId;
public static string DatabaseId { get { return _databaseId; } }
static ContainingClass()
{
_databaseId = CloudConfigurationManager.GetSetting("database");
}
}
Хотя и не полностью, как в случае, когда тип не имеет статического конструктора, "инициализаторы статического поля выполняются во время, зависящее от реализации, до первого использования статического поля этого класса".
Свойство C# 6.0 только для чтения создаст поле и вызовет инициализатор только один раз.
Тем не менее, это не равно, что у вас там. В вашем коде CloudConfigurationManager.GetSetting
будет вызываться только тогда, когда кто-то читает DatabaseId
свойство, но с "только для чтения авто свойство" CloudConfigurationManager.GetSetting
будет вызван во время самой инициализации класса.
Эта разница может / не может иметь значения. Это зависит. Если звонок дорогой, вы можете использовать Lazy<T>
что примерно равно тому, что у вас есть.
Он установит значение только один раз, и после этого просто прочитайте его.
Однако есть небольшая разница в том смысле, что у вас больше нет databaseId
поле. В первом примере вы проверяете id == null || id == ""
установить строку базы данных. Это означает, что если вы создаете новый экземпляр с databaseId
установить пустую строку, первый пример все равно получит идентификатор из настроек.
Второй пример, однако, увидит эту пустую строку в качестве допустимого значения и останется с ней.
Первый код:
if(id == null || id == "") // Get ID from settings
Второй код:
if(id == null) // Get ID from settings
Авто-свойство имеет автоматически вспомогательное поле. В этом случае это поле может быть назначено только из конструктора или из инициализатора автоматического свойства. Ваш новый код лучше первого. Это будет сделать только один звонок CloudConfigurationManager.GetSetting("database");
, В первом примере вы должны делать проверку каждый раз, когда ваша собственность get
называется.
вы можете изменить свой код на: (исправив несколько вещей по пути)(кстати, я предпочитаю использовать _DatabaseId для имени)
private string databaseId;
public string DatabaseId
{
get
{
return databaseId ??= CloudConfigurationManager.GetSetting("database");
}
}
Который вызовет метод GetSetting тогда и только тогда, когда свойство считывается.
Этот подход может быть полезен в ситуациях, когда у вас может быть много списков, и для их создания требуется много работы, поэтому вы не хотите этого делать, если кто-то действительно не использует его, на самом деле вы не хотите этого делать. Я даже не хочу создавать объект List.
Приятным побочным эффектом этого будет то, что не потребуется добавлять конструктор с кучей foo= new в нем. Он изолирован от определяемого свойства, и если один список обращается к другому списку, он будет каскадно создавать только то, что действительно необходимо для возврата запрашиваемых вами данных.
PS: Можно даже сделать это в 2 строки:
private string databaseId;
public string DatabaseId { get {return databaseId ??= CloudConfigurationManager.GetSetting("database");} }
Если вы хотите сохранить компактность...