Frage

Ich habe diese Methode vorläufig geschrieben:

public static Func<T> WeakCacheFor<T>( Func<T> provider ) where T: class
{
    var cache = new WeakReference(null);
    return () => {
        var x = (T)cache.Target;
        if( x == null )
        {
            x = provider();
            cache.Target = x;
        }
        return x;
    };
}

So ein wenig Hintergrund:

Ich habe einige langatmige Legacy Methoden, die ein bisschen wie folgt aussehen:

var id = GetDatabaseId();
if(a){
    var data = GetLoader().Init(id).GetData(); // expensive!
    // do stuff with data
}
if(b){
    // don't load data
}
... lots more variations, some contain GetLoader().Init(id).GetData(); some don't....

Meine mögliche Lösung ist, dies zu tun:

var id = GetDatabaseId();
var loadData = WeakCacheFor(() => GetLoader().Init(id).GetData());
if(a){
    var data = loadData();
    // do stuff with data
}
if(b){
    // don't load data
}
... lots more variations, some contain loadData(); some don't....

Meine Gedanken dazu:

  • Ich brauche nicht über den Rahmen dieses Verfahrens Aufruf cachen, so ist es in Ordnung, wenn die GC es, sobald die Methode zurückgibt, speichert
  • Wenn der Code einen Weg nimmt, der muss nicht um die Daten zu laden, wird der Treffer nicht anfallen
  • Wenn es um die Daten benötigt sie in der schwachen Referenz im Cache gespeichert werden, wenn sie wieder benötigt wird.
  • Wenn die GC Mitte sammelt, wird es keine Rolle, da es nur wieder geladen werden.

Meine Fragen:

  1. Wird dies tatsächlich funktionieren? - Gibt es etwas, das ich in der WeakCacheFor Methode verpasst haben, die einen starken Bezug verursachen könnten nämlich versehentlich gehalten werden
  2. Bin ich zu klug für meine eigenen guten? - Soll ich nur den Hit entstehen und die Daten in einer normalen lokalen Variablen zwischenzuspeichern, auch wenn es nicht benötigt wird

Ich vermute, dass ich zu klug sein zu sein, aber selbst wenn ich bin, ist dies wie eine Lösung für alle anderen scheinen die nützlicherweise in anderen Situationen angewendet werden kann ??

Update: Modifizierte Funktion, weil offenbar kann man nicht vertrauen .IsAlive

Update: Ich erkennen, dass die zurück Func außerhalb des Gültigkeitsbereiches am Ende des Verfahrens gehen, so ich brauche keinen WeakRef überhaupt und eine normale ref wird gut funktionieren. Ich wurde von einem Fall von Leiden „den Wald vor lauter Bäumen nicht sehen“, denke ich.

War es hilfreich?

Lösung

Ich sehe keinen Punkt in eine schwache Referenz. Nachdem Sie die Daten geladen haben, gibt es kaum einen Grund, um es wegzuwerfen, bis Sie sicher sind, dass es nicht sinnvoll ist mehr.

Was Sie implementieren ist eine Variation des verzögertes Laden Muster. Halten Sie sich an die einfachen Muster und nur einen regelmäßigen Verweis auf das Element verwenden:

public static Func<T> LazyLoad<T>(Func<T> provider) where T : class {
   T item = null;
   return () => {
      if (item == null) {
         item = provider();
      }
      return item;
   };
}

(Und ein kleiner Tipp:. Sie verwenden die var Schlüsselwort viel zu viel)

Andere Tipps

Ein paar allgemeine Bemerkungen:

  1. Ihre Lösung (wie auch meine) ist Thread nicht sicher.
  2. Es ist nicht darauf ausgelegt, mit IDisposable Objekten zu arbeiten.

Für die folgende Diskussion berücksichtigen:

foo = WeakCacheFor<Foo>(() => CreateFoo());

Case # 1: Verwenden foo als eine lange Lebensgröße (zum Beispiel ein Mitglied der langen Lebens Klasse oder eine globale Variable)

Ihre Lösung macht hier sence. Die Variable wird erstellen, wenn nötig, und zerstört, wenn Systemressourcen während der GC befreit.

Beachten Sie aber, dass, wenn foo ist zeit teuer, aber speicher billig, als es wahrscheinlich Sinn macht stattdessen Singletonmuster zu verwenden und laden Sie es einmal für die Dauer der Anwendung?

Case # 2. Mit foo als lokale Variable.

In diesem Fall ist es besser, Singletonmuster zu verwenden, schätze ich. Betrachten wir solches Beispiel:

static void Main(string[] args)
{
    MethodA(5, 7);
    MethodA(8, 9);
}

static void MethodA(int a, int b)
{
    var foo = WeakCacheFor<Foo>(() => new Foo());

    if (a > 3)
    {
        Use(foo);

        if (a * b == 35)
        {
            GC.Collect(); // Simulate GC
            Use(foo);
        }
        else if(b % 6 == 2)
        {
            Use(foo);
        }
    }
}

Die foo wird 3 Mal erstellt werden. Und 2 mal, wenn Sie die „Simulieren GC“ Linie kommentieren. verwenden diese Sie können auch nicht für IDisposable jeweiligen Kategorie.

Lassen Sie uns nun einen Singleton versuchen:

static void MethodA(int a, int b)
{
    using (var foo = new MySingleton<Foo>(() => new Foo()))
    {
        if (a > 3)
        {
            Use(foo);

            if (a * b == 35)
            {
                GC.Collect(); // Simulate GC
                Use(foo);
            }
            else if (b % 6 == 2)
            {
                Use(foo);
            }
        }
    }
}

Wie Sie sehen können, fast der Code nicht ändern, aber jetzt bekommen wir nur zwei Anrufe Ctor und IDisposable Unterstützung foo.

Eine grobe Singletons Implementierung:

class MySingleton<T> : IDisposable
    where T : class
{
    private T _value;
    private Func<T> _provider;

    public MySingleton(Func<T> provider)
    {
        _provider = provider;
    }

    public T Get()
    {
        if (_value == null)
        {
            _value = _provider();
        }

        return _value;
    }

    #region IDisposable Members

    public void Dispose()
    {
        if(_value == null)
            return;

        IDisposable disposable = _value as IDisposable;

        if(disposable != null)
            disposable.Dispose();
    }

    #endregion
}

Und der Rest des Codes:

class Foo : IDisposable
{
    public void Dispose() {}
}

static void Use(MySingleton<Foo> foo)
{
    foo.Get();
}

static void Use(Func<Foo> foo)
{
    foo();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top