2010-10-27 14 views
63

In C#,C# caricato in modo pigro Proprietà automatici

C'è un modo per trasformare una proprietà automatica in un pigro caricato immobile automatico 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 il difetto ed in cui gestisce il resto automaticamente ...

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

@Gabe: nota che la classe verrà chiamata una sola volta se non si ritira mai urne nulle. – RedFilter

+0

Ho scoperto che ... sembra che usi il modello singleton – ctorx

risposta

80

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 è possibile utilizzare il tipo di 4.0 Lazy<T> per creare questo schema

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

Questo codice sarà pigramente calcolare il valore della _someVariable la prima volta l'espressione Value si chiama. Verrà calcolato una sola volta e memorizzerà il valore per gli usi futuri della proprietà Value

+1

In realtà, mi sembra che Lazy implementa il modello singleton. Questo non è il mio obiettivo ... il mio obiettivo è creare una proprietà pigra e caricata che sia istanziata pigramente ma disposta insieme all'istanza della classe in cui vive. Pigro non sembra esibirsi in quel modo. – ctorx

+12

@ctorx Lazy non ha nulla a che fare con il modello singleton. Fa esattamente quello che vuoi che faccia. – Stijn

+4

Nota, 'SomeClass.IOnlyWantToCallYouOnce' nell'esempio deve essere statico per essere utilizzato con un inizializzatore di campo. –

2

I don Penso che questo sia possibile 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.

5

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

Potrebbe tuttavia essere possibile implementare qualcosa con gli Aspetti di PostSharp.

Dateci un'occhiata:

PostSharp

17

Probabilmente il più conciso si può ottenere è quello di utilizzare l'operatore null coalescenza:

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

Nel caso 'IOnlyWantToCallYouOnce' restituisce' null' lo chiamerà più di una volta. – JaredPar

+6

Quando si utilizza l'operatore null-coalescing, l'esempio sopra non funzionerà. La sintassi corretta è: '_SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); '- nota l'aggiunta della parentesi attorno all'impostazione' _SomeVariable' se è nullo. –

4

Ecco la mia implementazione di una soluzione al vostro 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<T> 
{ 
    bool _initialized = false; 
    T _result; 

    public T Value(Func<T> fn) 
    { 
     if (!_initialized) 
     { 
      _result = fn(); 
      _initialized = true; 
     } 
     return _result; 
    } 
} 

quindi di utilizzare:

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

C'è naturalmente l'overhead di passare il puntatore a funzione in giro, ma fa il lavoro per me e non si nota troppo in alto rispetto alla corsa il metodo più e più volte.

+0

Non avrebbe più senso dare la funzione al costruttore? In questo modo non lo creeresti in linea ogni volta e potresti eliminarlo dopo averlo usato la prima volta. –

+0

@ lund.mikkel sì, funzionerebbe anche questo. Possono essere casi d'uso per entrambi gli approcci. – deepee1

+5

Se si passa la funzione al costruttore, proprio come la classe Lazy di .Net, allora la funzione passata dovrà essere statica, so che questo non è adatto al mio design in molti casi. – crunchy

8

C'è una nuova funzionalità in C# 6 chiamato Expression Bodied Auto-Properties, che permette di scrivere un po 'più pulito:

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

    public string SomeVariable 
    { 
     get { return _someVariable.Value; } 
    } 
} 

ora può essere scritta come:

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

    public string SomeVariable => _someVariable.Value; 
} 
+0

Nell'ultima sezione di codice, l'inizializzazione non è in realtà pigra. 'IOnlyWantToCallYouOnce' verrebbe chiamato durante la costruzione ogni volta che la classe viene istanziata. –

+0

@TomBlodget Grazie, hai ragione –

+0

Quindi in altre parole questo non è pigro caricato? – Zapnologica

0

https://github.com/bcuff/AutoLazy utilizza Fody a darti una cosa del genere

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); 
     } 
    } 
} 
Problemi correlati