退订然后订阅 Console.CancelKeyPress

unsubscribe then subscribe to Console.CancelKeyPress

我注意到订阅然后取消订阅 Console.CancelKeyPress 会使下一次订阅失败。

更奇怪的是:从一个空的事件处理程序开始修复这个问题。

有人可以解释这种行为吗?

    class Program
    {
        static void Main(string[] args)
        {
            Console.TreatControlCAsInput = false;
// 2:       uncomment following for an unexpected fix...
            //Console.CancelKeyPress += (s, a) => { };

            Console.CancelKeyPress += Handler_1;
            Console.WriteLine("Press Ctr+C to prove that Handler_1 cancels key press.");
            Console.ReadLine();

            Application.DoEvents(); // make sure events are handled before unsubscribe
            Console.CancelKeyPress -= Handler_1;

// 1:       uncomment following to prove failure...
            //Console.CancelKeyPress += Handler_2;
            //Console.WriteLine("Press Ctr+C to prove that Handler_2 cancels key press.");
            //Console.ReadLine();

            Application.DoEvents(); // make sure events are handled
            Console.WriteLine("End of demo, type something to prove application is still responsive.");
            Console.ReadLine();
        }

        private static void Handler_1(object sender, ConsoleCancelEventArgs args)
        {
            args.Cancel = true;
            Console.WriteLine("Key press is canceled by Handler_1");
        }

        private static void Handler_2(object sender, ConsoleCancelEventArgs args)
        {
            args.Cancel = true;
            Console.WriteLine("Key press is canceled by Handler_2");
        }
    }

这是 CancelKeyPress 实现中的一个错误

public static event ConsoleCancelEventHandler CancelKeyPress {
                [System.Security.SecuritySafeCritical]  // auto-generated
                [ResourceExposure(ResourceScope.Process)]
                [ResourceConsumption(ResourceScope.Process)]
                add {
                    new UIPermission(UIPermissionWindow.SafeTopLevelWindows).Demand();

                    lock(InternalSyncObject) {
                        // Add this delegate to the pile.
                        _cancelCallbacks += value;

                        // If we haven't registered our control-C handler, do it.
                        if (_hooker == null) {
                            _hooker = new ControlCHooker();

// BUG: after you unsubscribe from CancelKeyPress it becomes null
// and when you subscribe to CancelKeyPress again the call below will never be called. In the Remove part they will not set _hooker to null.
                        _hooker.Hook();
                    }
                }
            }
            [System.Security.SecuritySafeCritical]  // auto-generated
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            remove {
                new UIPermission(UIPermissionWindow.SafeTopLevelWindows).Demand();

                lock(InternalSyncObject) {
                    // If count was 0, call SetConsoleCtrlEvent to remove cb.
                    _cancelCallbacks -= value;
                    Contract.Assert(_cancelCallbacks == null || _cancelCallbacks.GetInvocationList().Length > 0, "Teach Console::CancelKeyPress to handle a non-null but empty list of callbacks");
                    if (_hooker != null && _cancelCallbacks == null)
                        _hooker.Unhook();
//BUG: It Unhooks but does not set _hooker to null.
                    }
                }
            }