首页 > 解决方案 > 为什么垃圾收集、关闭和 lambda 禁用 prism 事件聚合器订阅?

问题描述

我有一个使用 PRISM 事件聚合器模式定义的简单事件

 public class TestEvent : PubSubEvent
    {
    }
    public static class PrismEvents
    {
        public static readonly IEventAggregator EventAggregator = new EventAggregator();
        public static readonly TestEvent EventTest = EventAggregator.GetEvent<TestEvent>();
    }

我有一个订阅者类,其中使用 lambda 订阅此事件。注意订阅代码中局部变量 (i) 的使用

public class SubScriber
    {
        public SubScriber()
        {
            int i = 5;
            PrismEvents.EventTest.Subscribe(() =>
            {
                Console.WriteLine("Event Fired");//not getting called
                i = 10; //commenting this line will execute the subscription code
            });
        }

    }

在发布者端创建订阅者,然后调用 GC,然后发布事件。

订阅代码没有被执行!

 class Program
    {
        static void Main(string[] args)
        {
            new SubScriber();
            GC.Collect(); //commenting this line will execute the subscription code
            PrismEvents.EventTest.Publish();
            Console.ReadKey();
        }

    }

几点

  1. 注释局部变量(i=10)的使用将解决问题。订阅代码将按预期执行

  2. 评论 GC.collect 将解决这个问题。订阅代码将按预期执行

这种行为的原因是什么?

标签: c#lambdagarbage-collectionclosureseventaggregator

解决方案


好问题。我没有所有答案,但 Prism 使用 Wea​​kReference。订阅在参数中使用的委托(操作)上创建一个 WeakReference。准确地说,WeakReference 是在委托的 Target 上进行的。这里有一些代码可以更好地理解发生了什么:

    public class SubScriber
    {
        public SubScriber()
        {
            int i = 5;
            Action action1 = () =>
            {
                Console.WriteLine("Event Fired action1");//not getting called
                i = 11; //commenting this line will execute the subscription code
            };

            Action action2 = () =>
            {
                Console.WriteLine("Event Fired action2");//will be called
            };

            Console.WriteLine("Target 1 = "+ action1.Target);
            Console.WriteLine("Target 2 = " + action2.Target);
            PrismEvents.EventTest.Subscribe(action1);
            PrismEvents.EventTest.Subscribe(action2);
        }

        ~SubScriber()
        {
            Console.WriteLine("SubScriber destructed");
        }
    }

    static void Main(string[] args)
    {
        new SubScriber();
        GC.Collect(); //commenting this line will execute the subscription code
        GC.WaitForPendingFinalizers(); // or Thread.Sleep(2000);
        Console.WriteLine("Publish");
        PrismEvents.EventTest.Publish();
        Console.WriteLine("Press a key to finish");
        Console.ReadKey();
    }

我们看到“SubScriber destructed”显示在“Publish”之前。有趣的是还可以使用 ILSpy 查看生成的内容:

public class SubScriber
    {
        [CompilerGenerated]
        private sealed class <>c__DisplayClass0_0
        {
            public int i;

            internal void ctor>b__0()
            {
                Console.WriteLine("Event Fired action1");
                this.i = 11;
            }
        }

        [CompilerGenerated]
        [Serializable]
        private sealed class <>c
        {
            public static readonly Program.SubScriber.<>c <>9 = new Program.SubScriber.<>c();

            public static Action <>9__0_1;

            internal void ctor>b__0_1()
            {
                Console.WriteLine("Event Fired action2");
            }
        }

        public SubScriber()
        {
            Program.SubScriber.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new Program.SubScriber.<>c__DisplayClass0_0();
            <>c__DisplayClass0_.i = 5;
            Action action = new Action(<>c__DisplayClass0_.<.ctor>b__0);
            Action arg_41_0;
            if ((arg_41_0 = Program.SubScriber.<>c.<>9__0_1) == null)
            {
                arg_41_0 = (Program.SubScriber.<>c.<>9__0_1 = new Action(Program.SubScriber.<>c.<>9.<.ctor>b__0_1));
            }
            Action action2 = arg_41_0;
            string arg_59_0 = "Target 1 = ";
            object expr_4D = action.Target;
            Console.WriteLine(arg_59_0 + ((expr_4D != null) ? expr_4D.ToString() : null));
            string arg_7B_0 = "Target 2 = ";
            object expr_6F = action2.Target;
            Console.WriteLine(arg_7B_0 + ((expr_6F != null) ? expr_6F.ToString() : null));
            Program.PrismEvents.EventTest.Subscribe(action);
            Program.PrismEvents.EventTest.Subscribe(action2);
        }

        ~SubScriber()
        {
            Console.WriteLine("SubScriber destructed");
        }
    }

我看到的不同之处在于,第二个动作有一个静态只读字段,它在委托上保存一个实例......

问候, 席芭莉丝


推荐阅读