【问题标题】:Is there a way to do a WeakList or WeakCollection (like WeakReference) in CLR?有没有办法在 CLR 中执行 WeakList 或 WeakCollection(如 WeakReference)?
【发布时间】:2010-05-14 20:41:39
【问题描述】:

使用List<WeakReference> 不会像我想要的那样工作。我想要的是,每当 WeakReferences 引用的对象被垃圾回收时,它们就会自动从列表中删除

ConditionalWeakTable<TKey,TValue> 也不让我满意,因为虽然它的键和值是弱引用和可收集的,但你无法枚举它们!

【问题讨论】:

  • 有趣的是到目前为止看到两个答案都有一些非全自动清除步骤。我需要花一些时间考虑它,但它实际上可能足以满足我的需要,即使它不是全自动的。
  • 清除是在枚举过程中最自然地完成的。唯一的其他选择是定期清除,在这种情况下,解决方案更像是“缓存”而不是“弱列表”。 WeakReference 不应该用于缓存;有更好的解决方案(例如,System.Runtime.Caching)。
  • 感谢有关 System.Runtime.Caching 的有趣建议。但是对于这个问题,我有一个特定的应用程序,我可以看到一些阻抗不匹配 - 1)我不需要或不想使用字符串键来获取项目,我只想能够按需迭代它们。 2)如果项目只是因为垃圾收集而不是其他杂项原因(比如缓存使用太多内存)而离开缓存,我可能会更高兴。
  • 这里是如何做到的。 See this StackOverflow answer.

标签: .net garbage-collection weak-references


【解决方案1】:

我同意实现WeakList<T> 是可能的,但我认为这并不完全简单。欢迎您使用我的实现hereWeakCollection<T> 类依赖于WeakReference<T>,后者又依赖于SafeGCHandle

【讨论】:

  • @stephen-cleary - 这似乎不在您最近的来源中,所以我很好奇您如何使用最新的 CLR 解决这个问题。
  • @Mike-EEE:对于蜉蝣,我使用Connected Properties。他们不支持枚举,但我从来不需要那种能力。
  • “这里”链接已失效。
【解决方案2】:

您可以轻松实现 WeakList<T> 类,该类将包装 List<WeakReference>

无法在垃圾回收时自动删除对象,因为无法检测到何时发生这种情况。但是,您可以通过检查WeakReference.IsAlive 属性在遇到“死”(垃圾收集)对象时删除它们。但是,我不推荐这种方法,因为从客户的角度来看,它可能会导致令人困惑的行为。相反,我建议实现一个 Purge 方法来删​​除死条目,您可以显式调用它。

这是一个示例实现:

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

该类使用以下类和扩展方法:

WeakReference&lt;T&gt;(只是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(与IList&lt;T&gt;.IndexOf 相同,但适用于IEnumerable&lt;T&gt;

    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(与IList&lt;T&gt;.CopyTo 相同,但适用于IEnumerable&lt;T&gt;

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

【讨论】:

  • 在 .net 4.0 中可以设计一个列表结构,该结构将通过使用 ConditionalWeakTable 将列表中的对象附加到具有终结器的其他对象来自动删除被 GC 处理的对象执行删除。请注意,这不应该使用数字索引列表来完成(因为没有办法以线程安全的方式完成删除),但可以使用按创建顺序或相反顺序迭代事物的链表来完成。不过,我不确定在什么情况下会在引用失效时主动删除它们......
  • ...会比记录自上次清除以来添加了多少项目,当时有多少还活着,并在自上次清除后添加的项目数时进行清除要好最后一个超过了当时还活着的数量(或者,每次添加一个项目时,扫描一些项目以删除,跟踪一个人在列表中的位置并在适当的时候从头开始重新开始)。这种方法会在任何给定时间将一些 WeakReference 对象不必要地保留在范围内,但该数字将相对于上次 GC 时存活的数字是有限的。
  • @supercat 考虑过,但不幸的是,终结器会带来额外的内存+性能成本,它们将从后台线程运行,因此需要您进行锁定或使用线程安全集合...(更多开销)
  • 还存在WeakReference&lt;T&gt; msdn.microsoft.com/en-us/library/gg712738%28v=vs.110%29.aspx 的默认通用实现
  • @ChieltenBrinke,确实如此,但是当我写这个答案时它并不存在;)
【解决方案3】:

对于需要在 .NET 2.0 或 3.5 中使用 ConditionalWeakTable 的任何人,这里有一个向后移植:https://github.com/theraot/Theraot/wiki/Features

【讨论】:

  • 嗨帕特里克。刚看到你的帖子。据我阅读 ConditionalWeakTable 的文档,即使有来自外部的对象的强引用,也不一定保持引用。您还有其他信息吗?
【解决方案4】:

如何使用 java.util.WeakHashMap 并将对象存储在键中?该值可以是任何虚拟对象。但是,您只能获得 WeakSet 功能,因为 Map 没有排序。

【讨论】:

  • 问题被标记为 .net,所以 java 没有帮助。
猜你喜欢
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-17
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多