ConditionalWeakTable - GC.Collect() 行为,为什么它没有按预期运行?

Eri*_*let 3 c# garbage-collection scope weak-references

为什么我的weakRef.Target在第二次射击时仍然存在?

这可能是一个错误吗?如果不是,错误在哪里?

结果:

weakRef.Target is alive = True, expected true because inst keep a hold on SomeClass.
weakRef.Target is alive = True, expected false, because there is no more ref on SomeClass.
Run Code Online (Sandbox Code Playgroud)

代码:

public static class DelegateKeeper
    {
        private static ConditionalWeakTable<object, Action> cwtAction = new ConditionalWeakTable<object, Action>();
        public static void KeepAlive(Action action) => cwtAction.Add(action.Target, action);
    }

    public class SomeClass
    {
        public void DoSomething() { }
    }

    public static class GcHelper
    {
        public static void Collect()
        {
            // OK surely overkill but just to make sure. I will reduce it when everyting will be understood.
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
            GC.WaitForPendingFinalizers();

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
            GC.WaitForPendingFinalizers();
        }
    }

    SomeClass instanceSomeClass;
    WeakReference<Action> weakRef;

    [TestMethod]
    public void TestLifeOfObject()
    {
        Init();
        GcHelper.Collect();
        Debug.WriteLine($"weakRef.Target is alive = {weakRef.TryGetTarget(out _)}, expected true because inst keep a hold on SomeClass.");

        RemoveLastReferenceOnSomeClass();
        GcHelper.Collect();
        Debug.WriteLine($"weakRef.Target is alive = {weakRef.TryGetTarget(out _)}, expected false, because there is no more ref on SomeClass.");
    }

    private void Init()
    {
        instanceSomeClass = new SomeClass();
        var action = instanceSomeClass.DoSomething;
        weakRef = new WeakReference<Action>(action);
        DelegateKeeper.KeepAlive(action);
    }

    private void RemoveLastReferenceOnSomeClass()
    {
        instanceSomeClass = null;
    }
Run Code Online (Sandbox Code Playgroud)

Cha*_*ace 5

文档对此并不清楚,但似乎确实强烈提及了要添加的内容,尽管只是很弱地提及。ConditionalWeakTablevaluekey

查看源代码,它使用了DependentHandle,文档中说:

如果没有其他强引用,以给定对象实例作为目标的值DependentHandle不会导致目标保持活动状态,但只要目标处于活动状态,它就会对依赖对象实例保持活动状态。

因此,由于您要添加action作为值,并且actionlambda 本身捕获inst,因此您对 进行了强引用inst

不过,您可以存储其他对象,您不在乎是否有强引用。请参阅此小提琴,其中 anew object()存储为值,并且它可以正常工作。我不知道你的用例,所以我不知道这是否适合你。

但我还要指出,微软声明

避免使用弱引用作为内存管理问题的自动解决方案。相反,开发一个有效的缓存策略来处理应用程序的对象。