C# Лениво Загруженные Автоматические Свойства
В C#
есть ли способ превратить автоматическое свойство в ленивое загруженное автоматическое свойство с указанным значением по умолчанию?
по сути, я пытаюсь превратить это...
private string _SomeVariable
public string SomeVariable
{
get
{
if(_SomeVariable == null)
{
_SomeVariable = SomeClass.IOnlyWantToCallYouOnce();
}
return _SomeVariable;
}
}
во что-то другое, где я могу указать значение по умолчанию, и он обрабатывает остальные автоматически...
[SetUsing(SomeClass.IOnlyWantToCallYouOnce())]
public string SomeVariable {get; private set;}
9 ответов:
нет нет нет. Авто-реализованные свойства только функция для реализации самых основных свойств: резервное поле с геттером и сеттером. Он не поддерживает эту функцию.
однако вы можете использовать 4.0
Lazy<T>
тип создать этот шаблонprivate Lazy<string> _someVariable =new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce); public string SomeVariable => _someVariable.Value;
этот код будет лениво вычислять значение
_someVariable
в первый разValue
выражение называется. Он будет рассчитан только один раз и будет кэшировать значение для будущего использованиеValue
свойства
вероятно, самое краткое, что вы можете получить, это использовать оператор null-coalescing:
get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); }
В C#6 появилась новая функция под названием Выражение Bodied Auto-Properties, что позволяет написать его немного чище:
public class SomeClass { private Lazy<string> _someVariable = new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce); public string SomeVariable { get { return _someVariable.Value; } } }
теперь можно записать так:
public class SomeClass { private Lazy<string> _someVariable = new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce); public string SomeVariable => _someVariable.Value; }
Не так, параметры для атрибутов должны быть постоянными по значению, вы не можете вызвать код (даже статический код).
однако вы можете реализовать что-то с аспектами PostSharp.
вот они:
вот моя реализация решения вашей проблемы. В основном идея-это свойство, которое будет установлено функцией при первом доступе, а последующие обращения будут давать то же возвращаемое значение, что и первое.
public class LazyProperty<T> { bool _initialized = false; T _result; public T Value(Func<T> fn) { if (!_initialized) { _result = fn(); _initialized = true; } return _result; } }
затем использовать:
LazyProperty<Color> _eyeColor = new LazyProperty<Color>(); public Color EyeColor { get { return _eyeColor.Value(() => SomeCPUHungryMethod()); } }
есть, конечно, накладные расходы на передачу указателя функции вокруг, но это делает работу для меня, и я не замечаю слишком много накладных расходов по сравнению с запуском метода снова и снова.
Я не думаю, что это возможно с чистым C#. Но вы могли бы сделать это с помощью IL rewriter, как PostSharp. Например, он позволяет добавлять обработчики до и после функций в зависимости от атрибутов.
Я большой поклонник этой идеи, и хотел бы предложить следующий фрагмент C#, который я назвал proplazy.фрагмент.(вы можете либо импортировать это, либо вставить его в стандартную папку, которую вы можете получить из диспетчера фрагментов)
вот пример ее вывода:
private Lazy<int> myProperty = new Lazy<int>(()=>1); public int MyProperty { get { return myProperty.Value; } }
вот содержимое файла сниппета: (сохранить как proplazy.сниппет)
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>proplazy</Title> <Shortcut>proplazy</Shortcut> <Description>Code snippet for property and backing field</Description> <Author>Microsoft Corporation</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>type</ID> <ToolTip>Property type</ToolTip> <Default>int</Default> </Literal> <Literal> <ID>field</ID> <ToolTip>The variable backing this property</ToolTip> <Default>myVar</Default> </Literal> <Literal> <ID>func</ID> <ToolTip>The function providing the lazy value</ToolTip> </Literal> <Literal> <ID>property</ID> <ToolTip>Property name</ToolTip> <Default>MyProperty</Default> </Literal> </Declarations> <Code Language="csharp"><![CDATA[private Lazy<$type$> $field$ = new Lazy<$type$>($func$); public $type$ $property$ { get{ return $field$.Value; } } $end$]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>
https://github.com/bcuff/AutoLazy использует Фоди, чтобы дать вам что-то вроде этого
public class MyClass { // This would work as a method, e.g. GetSettings(), as well. [Lazy] public static Settings Settings { get { using (var fs = File.Open("settings.xml", FileMode.Open)) { var serializer = new XmlSerializer(typeof(Settings)); return (Settings)serializer.Deserialize(fs); } } } [Lazy] public static Settings GetSettingsFile(string fileName) { using (var fs = File.Open(fileName, FileMode.Open)) { var serializer = new XmlSerializer(typeof(Settings)); return (Settings)serializer.Deserialize(fs); } } }
[Serializable] public class RaporImza { private readonly Func<ReportConfig> _getReportLayout; public RaporImza(Func<ReportConfig> getReportLayout) { _getReportLayout = getReportLayout; } private ReportConfig _getReportLayoutResult; public ReportConfig GetReportLayoutResult => _getReportLayoutResult ?? (_getReportLayoutResult = _getReportLayout()); public string ImzaAtanKisiAdi => GetReportLayoutResult.ReportSignatureName; public string ImzaAtanKisiUnvani => GetReportLayoutResult.ReportSignatureTitle; public byte[] Imza => GetReportLayoutResult.ReportSignature; }
и я звоню, как ниже
result.RaporBilgisi = new ExchangeProgramPersonAllDataModel.RaporImza(() => _reportConfigService.GetReportLayout(documentTypeId));