C # Lazy Loaded Automatic Properties

In C #,

C’è un modo per trasformare una proprietà automatica in una proprietà automatica caricata pigra con un valore predefinito specificato?

In sostanza, sto cercando di trasformare questo …

private string _SomeVariable public string SomeVariable { get { if(_SomeVariable == null) { _SomeVariable = SomeClass.IOnlyWantToCallYouOnce(); } return _SomeVariable; } } 

in qualcosa di diverso, dove posso specificare l’impostazione predefinita e gestisce automaticamente il resto …

 [SetUsing(SomeClass.IOnlyWantToCallYouOnce())] public string SomeVariable {get; private set;} 

No non c’è. Le proprietà implementate automaticamente funzionano solo per implementare le proprietà più basilari: il backing field con getter e setter. Non supporta questo tipo di personalizzazione.

Tuttavia è ansible utilizzare il tipo 4.0 Lazy per creare questo modello

 private Lazy _someVariable =new Lazy(SomeClass.IOnlyWantToCallYouOnce); public string SomeVariable => _someVariable.Value; 

Questo codice calcolerà il valore di _someVariable la prima volta che viene chiamata l’espressione Value . Verrà calcolato una sola volta e memorizzerà il valore in cache per gli usi futuri della proprietà Value

Probabilmente il più sintetico che puoi ottenere è usare l’operatore null-coalescing:

 get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); } 

C’è una nuova funzionalità in C # 6 chiamata Expression Bodied Auto-Properties , che ti permette di scriverlo un po ‘più pulito:

 public class SomeClass { private Lazy _someVariable = new Lazy(SomeClass.IOnlyWantToCallYouOnce); public string SomeVariable { get { return _someVariable.Value; } } } 

Ora può essere scritto come:

 public class SomeClass { private Lazy _someVariable = new Lazy(SomeClass.IOnlyWantToCallYouOnce); public string SomeVariable => _someVariable.Value; } 

Non così, i parametri per gli attributi devono essere di valore costante, non è ansible chiamare il codice (anche il codice statico).

Tuttavia, potresti essere in grado di implementare qualcosa con gli Aspetti di PostSharp.

Controllali:

PostSharp

Ecco la mia implementazione di una soluzione al tuo problema. Fondamentalmente l’idea è una proprietà che verrà impostata da una funzione al primo accesso e gli accessi successivi produrranno lo stesso valore di ritorno del primo.

 public class LazyProperty { bool _initialized = false; T _result; public T Value(Func fn) { if (!_initialized) { _result = fn(); _initialized = true; } return _result; } } 

Quindi utilizzare:

 LazyProperty _eyeColor = new LazyProperty(); public Color EyeColor { get { return _eyeColor.Value(() => SomeCPUHungryMethod()); } } 

C’è ovviamente il sovraccarico di passare il puntatore della funzione in giro, ma fa il lavoro per me e non noto troppi sovraccarichi rispetto all’esecuzione del metodo più e più volte.

Non penso sia ansible con il puro C #. Ma potresti farlo usando un rewriter IL come PostSharp . Ad esempio, consente di aggiungere gestori prima e dopo le funzioni a seconda degli attributi.

Sono un grande fan di questa idea e vorrei offrire il seguente snippet C # che ho chiamato proplazy.snippet. (Puoi importarlo o incollarlo nella cartella standard che puoi ottenere da Snippet Manager)

Ecco un esempio del suo output:

 private Lazy myProperty = new Lazy(()=>1); public int MyProperty { get { return myProperty.Value; } } 

Ecco il contenuto del file snippet: (salva come proplazy.snippet)

    
proplazy proplazy Code snippet for property and backing field Microsoft Corporation Expansion
type Property type int field The variable backing this property myVar func The function providing the lazy value property Property name MyProperty $field$ = new Lazy<$type$>($func$); public $type$ $property$ { get{ return $field$.Value; } } $end$]]>

https://github.com/bcuff/AutoLazy usa Fody per darti qualcosa del genere

 public class MyClass { // This would work as a method, eg 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 _getReportLayout; public RaporImza(Func 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; } 

e io chiamo come un muggito

 result.RaporBilgisi = new ExchangeProgramPersonAllDataModel.RaporImza(() => _reportConfigService.GetReportLayout(documentTypeId)); 

L’ho fatto in questo modo:

 public static class LazyCachableGetter { private static ConditionalWeakTable> Instances = new ConditionalWeakTable>(); public static R LazyValue(this T obj, Func factory, [CallerMemberName] string prop = "") { R result = default(R); if (!ReferenceEquals(obj, null)) { if (!Instances.TryGetValue(obj, out var cache)) { cache = new ConcurrentDictionary(); Instances.Add(obj, cache); } if (!cache.TryGetValue(prop, out var cached)) { cache[prop] = (result = factory()); } else { result = (R)cached; } } return result; } } 

e più tardi puoi usarlo come

  public virtual bool SomeProperty => this.LazyValue(() => { return true; }); 

Se si utilizza un costruttore durante l’inizializzazione pigra, anche le seguenti estensioni potrebbero essere utili

 public static partial class New { public static T Lazy(ref T o) where T : class, new() => o ?? (o = new T()); public static T Lazy(ref T o, params object[] args) where T : class, new() => o ?? (o = (T) Activator.CreateInstance(typeof(T), args)); } 

uso

  private Dictionary _cache; public Dictionary Cache => New.Lazy(ref _cache); /* _cache ?? (_cache = new Dictionary()); */