2010-10-27 2 views
63

In C#C# Faule Loaded Automatische Eigenschaften

Gibt es eine Möglichkeit, eine automatische Eigenschaft in eine faule geladene automatische Eigenschaft mit einem bestimmten Standardwert zu verwandeln?

Im Grunde versuche ich, diese zu drehen ...

private string _SomeVariable 

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

      return _SomeVariable; 
    } 
} 

in etwas anderes, wo ich den Standard festlegen kann und es übernimmt den Rest automatisch ...

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

@Gabe: Beachten Sie, dass die Klasse nur einmal aufgerufen wird, wenn es nie ret Urnen null. – RedFilter

+0

Ich entdeckte, dass ... es scheint, dass das Singleton-Muster verwendet wird – ctorx

Antwort

80

Nein, gibt es nicht. Automatisch implementierte Eigenschaften funktionieren nur, um die grundlegendsten Eigenschaften zu implementieren: Hintergrundfeld mit Getter und Setter. Diese Art der Anpassung wird nicht unterstützt.

auch immer Sie den 4.0 Lazy<T> Typ verwenden, können diese Muster

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

Dieser Code zu erstellen den Wert von _someVariable das erste Mal die Value Ausdruck heißt lazily berechnen. Es wird nur einmal berechnet und speichert den Wert für zukünftige Verwendungen der Value Eigenschaft

+1

Eigentlich sieht es für mich wie Lazy implementiert das Singleton-Muster. Das ist nicht mein Ziel ... mein Ziel ist es, eine lazy loaded Eigenschaft zu erstellen, die lazy instanziiert wird, aber zusammen mit der Instanz der Klasse, in der sie lebt. Lazy scheint nicht so zu sein. – ctorx

+12

@ctorx Lazy hat nichts mit dem Singleton-Muster zu tun. Es macht genau das, was Sie wollen. – Stijn

+4

Hinweis: 'SomeClass.IOnlyWantToCallYouOnce' in Ihrem Beispiel muss statisch sein, um mit einem Feldinitialisierer verwendet zu werden. –

2

I don Ich denke, das ist mit reinem C# möglich. Aber Sie könnten es mit einem IL-Rewriter wie PostSharp tun. Zum Beispiel können Sie Handler vor und nach Funktionen abhängig von Attributen hinzufügen.

5

Nicht so, Parameter für Attribute müssen in Wert konstant sein, Sie können nicht Code aufrufen (auch statische Code).

Sie können jedoch möglicherweise etwas mit PostSharp Aspects implementieren.

Überprüfen Sie sie heraus:

PostSharp

17

wohl prägnanteste Sie ist bekommen die Null-Koaleszenz-Operator zu verwenden:

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

Wenn 'IOnlyWantToCallYouOnce' den Wert 'null' zurückgibt, wird es mehr als einmal aufgerufen. – JaredPar

+6

Bei Verwendung des Nullkoaleszenzoperators schlägt das obige Beispiel fehl. Die korrekte Syntax lautet: '_SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); '- Beachten Sie das Hinzufügen der Klammer um die Einstellung' _SomeVariable', wenn es null ist. –

4

Hier ist meine Implementierung eines um Ihr Problem zu lösen. Im Grunde ist die Idee eine Eigenschaft, die beim ersten Zugriff durch eine Funktion gesetzt wird, und nachfolgende Zugriffe ergeben den gleichen Rückgabewert wie der erste.

public class LazyProperty<T> 
{ 
    bool _initialized = false; 
    T _result; 

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

Dann zu verwenden:

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

natürlich Es ist der Overhead der Funktionszeiger um zugeben, aber es macht den Job für mich und ich Overhead nicht zu viel bemerken, im Vergleich zu Laufen die Methode immer und immer wieder.

+0

Wäre es nicht sinnvoller, dem Konstruktor die Funktion zu geben? Auf diese Weise würden Sie es nicht jedes Mal inline erstellen, und Sie könnten es nach dem ersten Gebrauch entsorgen. –

+0

@ lund.mikkel Ja, das würde auch funktionieren. Kann für beide Ansätze Anwendungsfälle sein. – deepee1

+5

Wenn Sie die Funktion an den Konstruktor übergeben, ähnlich der Lazy-Klasse von .Net, dann muss die übergebene Funktion statisch sein, ich weiß, dass dies in vielen Fällen nicht zu meinem Design passt. – crunchy

8

Es ist eine neue Funktion in C# 6 Expression Bodied Auto-Properties genannt, die man ein wenig sauberer zu schreiben erlaubt:

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

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

jetzt kann geschrieben werden:

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

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

Im letzten Abschnitt Code ist die Initialisierung nicht wirklich faul. 'IOnlyWantToCallYouOnce' würde jedes Mal, wenn die Klasse instanziiert wird, während der Konstruktion aufgerufen. –

+0

@TomBlodget Danke, du hast Recht –

+0

Also in anderen Worten ist dies nicht faul geladen? – Zapnologica

0

https://github.com/bcuff/AutoLazy verwendet Fody zu geben Sie so etwas

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