【问题标题】:Declare EventHandlers as Static or Non-Static in WPF Controls在 WPF 控件中将 EventHandlers 声明为静态或非静态
【发布时间】:2011-06-04 18:34:55
【问题描述】:

我正在创建一个具有命令行为的自定义控件,但遇到了一些奇怪的事情。我发现的一些文章将 CanExecuteChangedHandler EventHandler 声明为静态的,而其他文章则声明为非静态的。 Microsoft 的 SDK 文档显示为静态,但当我将其声明为静态时,使用多个控件时会出现奇怪的行为。

private static EventHandler canExecuteChangedHandler;

private void AddSecureCommand(ISecureCommand secureCommand)
{
    canExecuteChangedHandler = new EventHandler(CanExecuteChanged);
    securityTypeChangedHandler = new EventHandler(SecurityTypeChanged);

    if (secureCommand != null)
    {
        secureCommand.CanExecuteChanged += canExecuteChangedHandler;
        secureCommand.SecurityTypeChanged += securityTypeChangedHandler;
    }
}

有人知道正确的方法吗?我是否做错了什么导致静态 EventHandler 无法工作?

【问题讨论】:

    标签: c# wpf wpf-controls binding


    【解决方案1】:

    保留EventHandler 的本地副本的原因是WPF 命令子系统在内部使用弱引用,因此我们需要保留对添加到CanExecuteChanged 事件的特定委托对象的引用。事实上,无论何时我们添加到任何指挥子系统事件中,我们也应该遵守这种做法,就像您对 SecurityTypeChanged 所做的那样。

    您的问题的简短回答是canExecuteChangedHandler 可以是静态的,但您必须小心只初始化一次。它可以是静态的原因是,如果CanExecuteChanged 是静态的,所有new EventHandler(CanExecuteChanged) 都会做同样的事情。初始化一次的原因是不同的实例不同。

    具有正确只读语义的私有属性是:

    static EventHandler canExecuteChangedHandler
    {
        get
        {
            if (internalCanExecuteChangedHandler == null)
                internalCanExecuteChangedHandler = new EventHandler(CanExecuteChanged);
            return internalCanExecuteChangedHandler;
        }
    
    }
    static EventHandler internalCanExecuteChangedHandler;
    

    但这仅适用于CanExecuteChanged 是静态的。如果不是,则删除 static 限定符。无论哪种情况,您都必须小心使用该属性。

    在此特定示例中,第二次调用 AddSecureCommand 第一次调用canExecuteChangedHandler 有被垃圾收集的风险。

    最后,如果这一切听起来像黑魔法,这里有一个代码示例来说明正在发生的事情。

    public class Container
    {
        private WeakReference reference;
        public object Object
        {
            get { return reference.IsAlive ? reference.Target : null; }
            set { reference = new WeakReference(value); }
        }
    }
    
    public class DelegateTest
    {
        private EventHandler eventHandler;
        private Container container1;
        private Container container2;
    
        void MyEventHandler(object sender, EventArgs args)
        {
        }
    
        public DelegateTest()
        {
            this.eventHandler = new EventHandler(MyEventHandler);
            this.container1 = new Container { Object = this.eventHandler };
            this.container2 = new Container { Object = new EventHandler(MyEventHandler) };
            GC.Collect();
            Console.WriteLine("container1: {0}", this.container1.Object == null);
            Console.WriteLine("container2: {0}", this.container2.Object == null);
        }
    }
    

    这会产生这个输出:

    container1: False
    container2: True
    

    这表明在垃圾收集期间,第二个容器的EventHandlergarbage-collected “从它下面出来”。这是设计使弱引用的工作方式以及您需要自己保留对它的引用的解释。

    【讨论】:

    • 我了解垃圾收集的原因,并且我知道我必须在类级别声明 EventHandler。我遇到的问题是,当我多次实例化该类时,当它是静态的时,我会出现奇怪的行为。
    • 我的意思是添加一个换行符,它提交了评论。我试图编辑上面的评论,但显然我只有 5 分钟的时间来更新它。在上面详细说明......我遇到的问题是当我多次实例化类(或控件)时,当它是静态的时,我会得到奇怪的行为。在做了更多研究之后,我发现 EventHandler 委托的目标是特定于实例的。在我的情况下,使用静态 EventHandler 会导致问题。我可以看到有时可能会使用静态 EventHandler,但我想它在我的情况下不起作用。
    • 总结你的观点:事件处理程序引用只有在方法是静态的时候才能是静态的。在将其添加到指挥事件后,您仍需注意不要对其进行修改。我会更新答案。
    • 评论垃圾收集,如果我不将 EventHandler 创建为静态的,那么每个类实例将创建一个实例,这也将阻止垃圾收集。感谢您提供所有信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多