2010-05-14 9 views
14

Mit einem List<WeakReference> wird nicht funktionieren, wie ich will. Was ich will ist, dass WeakReferences automatisch aus der Liste entfernt werden, wenn das Objekt, auf das sie verweisen, Garbage Collection ist.Gibt es eine Möglichkeit, eine WeakList oder WeakCollection (wie WeakReference) in CLR zu tun?

ConditionalWeakTable<TKey,TValue> befriedigt mich auch nicht, denn obwohl seine Schlüssel und Werte schwach referenziert und sammelbar sind, können Sie sie nicht aufzählen!

+0

Interessante zwei Antworten zu sehen, so weit haben beide eine nicht-vollautomatische Reinigungsschritt. Ich muss einige Zeit darüber nachdenken, aber es könnte tatsächlich gut genug für das sein, was ich brauche, auch wenn es nicht vollautomatisch ist. –

+1

Das Säubern geschieht am natürlichsten während der Aufzählung. Die einzige andere Option ist eine periodische Bereinigung, in welchem ​​Fall die Lösung eher ein "Cache" als eine "schwache Liste" wird. 'WeakReference' soll nicht zum Caching verwendet werden; Dafür gibt es bessere Lösungen (z. B. [System.Runtime.Caching] (http://msdn.microsoft.com/en-us/library/system.runtime.caching.aspx)). –

+0

Danke für den interessanten Vorschlag über System.Runtime.Caching. Aber ich hatte eine bestimmte Anwendung für diese Frage im Sinn, und ich kann einige Impendance Mismatches sehen - 1) Ich brauche nicht oder möchte String-Schlüssel verwenden, um Elemente zu bekommen, ich möchte nur in der Lage sein, sie auf Nachfrage zu iterieren.2) Ich wäre wahrscheinlich glücklicher, wenn Elemente den Cache nur aufgrund von Garbage Collection verlassen würden, und nicht aus anderen Gründen (wie Cache, der zu viel Speicher verbraucht). –

Antwort

7

Ich bin damit einverstanden, dass eine WeakList<T> ist möglich umzusetzen, aber Ich glaube nicht, dass es genau einfach ist. Sie können gerne meine Implementierung here verwenden. Die WeakCollection<T> Klasse ist abhängig von WeakReference<T>, was wiederum von SafeGCHandle abhängt.

+0

Es ist ein Codeplex-Projekt ?! Genial. Vielen Dank. :-) –

+0

@ stephen-cleary - das scheint nicht in Ihrer neuesten Quelle zu sein, also bin ich neugierig darauf, wie Sie dieses Problem mit der neuesten CLR nähern. –

+2

@ Mike-EEE: Für Ephemerons verwende ich [Verbundene Eigenschaften] (http://connectedproperties.codeplex.com/). Sie unterstützen keine Aufzählung, aber ich habe diese Fähigkeit nie benötigt. –

6

Sie könnten leicht eine WeakList<T> Klasse implementieren, die eine List<WeakReference> umhüllen würde.

Es gibt keine Möglichkeit, Objekte automatisch zu entfernen, wenn sie nicht ordnungsgemäß verarbeitet werden, da dies nicht erkannt werden kann. Sie können jedoch "tote" (Garbage Collection) Objekte entfernen, wenn Sie auf diese stoßen, indem Sie die Eigenschaft WeakReference.IsAlive überprüfen. Ich würde diesen Ansatz jedoch nicht empfehlen, da dies aus Sicht des Kunden zu einem verwirrenden Verhalten führen könnte. Stattdessen würde ich empfehlen, eine Purge-Methode zu implementieren, um tote Einträge zu entfernen, die Sie explizit aufrufen würden.

Hier ist eine Beispielimplementierung:

public class WeakList<T> : IList<T> 
{ 
    private List<WeakReference<T>> _innerList = new List<WeakReference<T>>(); 

    #region IList<T> Members 

    public int IndexOf(T item) 
    { 
     return _innerList.Select(wr => wr.Target).IndexOf(item); 
    } 

    public void Insert(int index, T item) 
    { 
     _innerList.Insert(index, new WeakReference<T>(item)); 
    } 

    public void RemoveAt(int index) 
    { 
     _innerList.RemoveAt(index); 
    } 

    public T this[int index] 
    { 
     get 
     { 
      return _innerList[index].Target; 
     } 
     set 
     { 
      _innerList[index] = new WeakReference<T>(value); 
     } 
    } 

    #endregion 

    #region ICollection<T> Members 

    public void Add(T item) 
    { 
     _innerList.Add(new WeakReference<T>(item)); 
    } 

    public void Clear() 
    { 
     _innerList.Clear(); 
    } 

    public bool Contains(T item) 
    { 
     return _innerList.Any(wr => object.Equals(wr.Target, item)); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     _innerList.Select(wr => wr.Target).CopyTo(array, arrayIndex); 
    } 

    public int Count 
    { 
     get { return _innerList.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public bool Remove(T item) 
    { 
     int index = IndexOf(item); 
     if (index > -1) 
     { 
      RemoveAt(index); 
      return true; 
     } 
     return false; 
    } 

    #endregion 

    #region IEnumerable<T> Members 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _innerList.Select(x => x.Target).GetEnumerator(); 
    } 

    #endregion 

    #region IEnumerable Members 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 

    #endregion 

    public void Purge() 
    { 
     _innerList.RemoveAll(wr => !wr.IsAlive); 
    } 
} 

Diese Klasse verwendet die folgenden Klassen und Erweiterungsmethoden:

WeakReference<T> (nur eine stark typisierte Wrapper um WeakReference)

[Serializable] 
public class WeakReference<T> : WeakReference 
{ 
    public WeakReference(T target) 
     : base(target) 
    { 
    } 

    public WeakReference(T target, bool trackResurrection) 
     : base(target, trackResurrection) 
    { 
    } 

    public WeakReference(SerializationInfo info, StreamingContext context) 
     : base(info, context) 
    { 
    } 

    public new T Target 
    { 
     get 
     { 
      return (T)base.Target; 
     } 
    } 
} 

IndexOf (gleich wie IList<T>.IndexOf, arbeitet aber auf einem IEnumerable<T>)

public static int IndexOf<T>(this IEnumerable<T> source, T item) 
    { 
     var entry = source.Select((x, i) => new { Value = x, Index = i }) 
        .Where(x => object.Equals(x.Value, item)) 
        .FirstOrDefault(); 
     return entry != null ? entry.Index : -1; 
    } 

CopyTo (gleiche wie IList<T>.CopyTo, arbeitet aber auf einem IEnumerable<T>)

public static void CopyTo<T>(this IEnumerable<T> source, T[] array, int startIndex) 
    { 
     int lowerBound = array.GetLowerBound(0); 
     int upperBound = array.GetUpperBound(0); 
     if (startIndex < lowerBound) 
      throw new ArgumentOutOfRangeException("startIndex", "The start index must be greater than or equal to the array lower bound"); 
     if (startIndex > upperBound) 
      throw new ArgumentOutOfRangeException("startIndex", "The start index must be less than or equal to the array upper bound"); 

     int i = 0; 
     foreach (var item in source) 
     { 
      if (startIndex + i > upperBound) 
       throw new ArgumentException("The array capacity is insufficient to copy all items from the source sequence"); 
      array[startIndex + i] = item; 
      i++; 
     } 
    } 
+2

Es wäre möglich, in .NET 4.0 eine Listenstruktur zu entwerfen, die automatisch Objekte entfernt, die mit einem 'ConditionalWeakTable' versehen wurden, um die Objekte in der Liste an andere Objekte anzuhängen, wobei Finalizer das Entfernen ausführen würden. Beachten Sie, dass dies nicht mit einer numerisch indizierten Liste erfolgen soll (da es keine Möglichkeit gibt, die Entfernung in threadsicherer Weise durchzuführen), sondern mit einer verknüpften Liste, die die Dinge in der Reihenfolge oder umgekehrten Reihenfolge der Erstellung iteriert. Ich bin mir jedoch nicht sicher, in welchen Fällen Referenzen aktiv entfernt werden, wenn sie tot sind ... – supercat

+0

... wäre besser als die Anzahl der Elemente, die seit der letzten Säuberung hinzugefügt wurden, zu zählen und wie viele am Leben waren dieser Zeit, und eine Bereinigung durchführen, wenn die Anzahl der Elemente, die seit dem letzten hinzugefügt wurden, die Anzahl überschreitet, die dann lebendig war (oder alternativ jedes Mal, wenn ein Element hinzugefügt wird, ein paar Elemente zur Entfernung scannt und seine Position in der ggf. auflisten und neu starten. Ein solcher Ansatz würde zu einem gegebenen Zeitpunkt einige "WeakReference" -Objekte unnötig im Bereich halten, aber die Anzahl wäre relativ zu der Anzahl beschränkt, die bei der letzten GC am Leben war. – supercat

+0

@supercat Betrachtet, aber leider Finalizer kommen mit zusätzlichen Speicher + Performance-Kosten und sie werden von einem Hintergrund-Thread laufen und erfordern Sie zu sperren oder verwenden thread-sichere Sammlungen ... (mehr Overhead) –

0

Für alle benötigen eine ConditionalWeakTable in .NET zu verwenden, 2.0 oder 3.5 gibt es eine Rückportierung von hier: https://github.com/theraot/Theraot/wiki/Features

+0

Hallo Patrick. Habe gerade deinen Post gesehen. Soweit ich die Dokumente der ConditionalWeakTable gelesen habe, muss man nicht unbedingt die Referenzen behalten, auch wenn es einen starken Bezug zum Objekt von außen gibt. Haben Sie andere Informationen? – msedi

Verwandte Themen