С# Lazy Loaded Automatic Properties
В С#,
Есть ли способ превратить автоматическое свойство в ленивое загруженное автоматическое свойство с заданным значением по умолчанию?
По существу, я пытаюсь включить это...
private string _SomeVariable
public string SomeVariable
{
get
{
if(_SomeVariable == null)
{
_SomeVariable = SomeClass.IOnlyWantToCallYouOnce();
}
return _SomeVariable;
}
}
во что-то другое, где я могу указать значение по умолчанию, и оно автоматически обрабатывает остальные...
[SetUsing(SomeClass.IOnlyWantToCallYouOnce())]
public string SomeVariable {get; private set;}
Ответы
Ответ 1
Нет там нет. Автоматически реализуемые свойства функционируют только для реализации самых основных свойств: вспомогательного поля с геттером и сеттером. Он не поддерживает этот тип настройки.
Однако вы можете использовать тип 4.0 Lazy<T>
для создания этого шаблона
private Lazy<string> _someVariable =new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce);
public string SomeVariable => _someVariable.Value;
Этот код будет лениво вычислять значение _someVariable
при первом вызове выражения Value
. Он будет рассчитан только один раз и кеширует значение для будущего использования свойства Value
.
Ответ 2
Вероятнее всего, наиболее кратким вы можете получить использование оператора с нулевым коалесцированием:
get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); }
Ответ 3
В С# 6 есть новая функция, называемая Expression 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;
}
Ответ 4
Не так, параметры для атрибутов должны быть постоянными по значению, вы не можете вызывать код (даже статический код).
Однако вы можете реализовать что-то с помощью PostSharp Aspects.
Проверьте их:
PostSharp
Ответ 5
Вот моя реализация решения вашей проблемы. В принципе идея - это свойство, которое будет задано функцией при первом доступе и последующем доступе, даст такое же возвращаемое значение, как и первое.
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());
}
}
Конечно, есть накладные расходы на передачу указателя функции, но он выполняет эту работу для меня, и я не замечаю слишком много накладных расходов по сравнению с запуском метода снова и снова.
Ответ 6
Я большой поклонник этой идеи и хотел бы предложить следующий фрагмент кода С#, который я назвал proplazy.snippet. (Вы можете импортировать его или вставить в стандартную папку, которую вы можете получить из Диспетчера фрагментов)
Вот пример его вывода:
private Lazy<int> myProperty = new Lazy<int>(()=>1);
public int MyProperty { get { return myProperty.Value; } }
Здесь содержимое файла фрагмента: (сохранить как proplazy.snippet)
<?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>
Ответ 7
Я не думаю, что это возможно с чистым С#. Но вы можете сделать это с помощью переписывающего IL, например PostSharp. Например, он позволяет добавлять обработчики до и после функций в зависимости от атрибутов.
Ответ 8
Я сделал это так:
public static class LazyCachableGetter
{
private static ConditionalWeakTable<object, IDictionary<string, object>> Instances = new ConditionalWeakTable<object, IDictionary<string, object>>();
public static R LazyValue<T, R>(this T obj, Func<R> factory, [CallerMemberName] string prop = "")
{
R result = default(R);
if (!ReferenceEquals(obj, null))
{
if (!Instances.TryGetValue(obj, out var cache))
{
cache = new ConcurrentDictionary<string, object>();
Instances.Add(obj, cache);
}
if (!cache.TryGetValue(prop, out var cached))
{
cache[prop] = (result = factory());
}
else
{
result = (R)cached;
}
}
return result;
}
}
и позже вы можете использовать его как
public virtual bool SomeProperty => this.LazyValue(() =>
{
return true;
});
Ответ 9
https://github.com/bcuff/AutoLazy использует Fody, чтобы дать вам что-то вроде этого
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);
}
}
}
Ответ 10
[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));
Ответ 11
Если вы используете конструктор во время отложенной инициализации, вам могут быть полезны следующие расширения
public static partial class New
{
public static T Lazy<T>(ref T o) where T : class, new() => o ?? (o = new T());
public static T Lazy<T>(ref T o, params object[] args) where T : class, new() =>
o ?? (o = (T) Activator.CreateInstance(typeof(T), args));
}
Usage
Usage
private Dictionary<string, object> _cache;
public Dictionary<string, object> Cache => New.Lazy(ref _cache);
/* _cache ?? (_cache = new Dictionary<string, object>()); */