在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;}
答案 0 :(得分:98)
不,没有。自动实现的属性仅用于实现最基本的属性:带有getter和setter的后备字段。它不支持这种类型的自定义。
但是,您可以使用4.0 Lazy<T>
类型来创建此模式
private Lazy<string> _someVariable =new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce);
public string SomeVariable => _someVariable.Value;
此代码将在第一次调用_someVariable
表达式时懒惰地计算Value
的值。它只会被计算一次并将缓存该值以供将来使用Value
属性
答案 1 :(得分:27)
您可以获得的最简洁的可能是使用null-coalescing运算符:
get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); }
答案 2 :(得分:12)
C#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;
}
答案 3 :(得分:5)
答案 4 :(得分: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());
}
}
当然有传递函数指针的开销,但是它为我完成了工作,而且与一遍又一遍地运行方法相比,我没有注意到太多的开销。
答案 5 :(得分:4)
运算符??=在C#8.0和更高版本中可用,因此您现在可以更加简洁:
private string _someVariable;
public string SomeVariable => _someVariable ??= SomeClass.IOnlyWantToCallYouOnce();
答案 6 :(得分:3)
我是这个想法的忠实拥护者,并希望提供以下我称为proplazy.snippet的C#代码段。(您可以将其导入或粘贴到标准文件夹中,该文件夹可以从代码段中获取)经理)
以下是其输出示例:
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 :(得分:2)
我不认为纯C#是可能的。但你可以使用像PostSharp这样的IL重写器来做到这一点。例如,它允许您根据属性在函数之前和之后添加处理程序。
答案 8 :(得分:0)
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);
}
}
}
答案 9 :(得分:0)
[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));
答案 10 :(得分:0)
我是这样做的:
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;
});
答案 11 :(得分:0)
如果您在延迟初始化期间使用构造函数,则以下扩展名也可能会有所帮助
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));
}
用法
private Dictionary<string, object> _cache;
public Dictionary<string, object> Cache => New.Lazy(ref _cache);
/* _cache ?? (_cache = new Dictionary<string, object>()); */