C# 多次防止相同的事件处理程序分配

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/399648/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 01:57:20  来源:igfitidea点击:

Preventing same Event handler assignment multiple times

c#events

提问by

If I am assigning an event handler at runtime and it is in a spot that can be called multiple times, what is the recommended practice to prevent multiple assignments of the same handler to the same event.

如果我在运行时分配一个事件处理程序并且它位于可以多次调用的位置,那么推荐的做法是防止将同一处理程序多次分配给同一事件。

object.Event += MyFunction

Adding this in a spot that will be called more than once will execute the handler 'n' times (of course).

在一个将被多次调用的地方添加它会执行处理程序“n”次(当然)。

I have resorted to removing any previous handler before trying to add via

在尝试通过添加之前,我已经诉诸于删除任何以前的处理程序

object.Event -= MyFunction; 

object.Event += MyFunction;

This works but seems off somehow. Any suggestions on proper handling ;) of this scenario.

这有效,但似乎以某种方式关闭。有关正确处理 ;) 这种情况的任何建议。

采纳答案by Jon Skeet

Baget is right about using an explicitly implemented event (although there's a mixture there of explicit interface implementation and the full event syntax). You can probably get away with this:

Baget 使用显式实现的事件是正确的(尽管显式接口实现和完整的事件语法混合使用)。你可能可以摆脱这个:

private EventHandler foo;

public event EventHandler Foo
{
    add
    {
        // First try to remove the handler, then re-add it
        foo -= value;
        foo += value;
    }
    remove
    {
        foo -= value;
    }
}

That may have some odd edge cases if you ever add or remove multicast delegates, but that's unlikely. It also needs careful documentation as it's not the way that events normally work.

如果您添加或删除多播委托,那可能会有一些奇怪的边缘情况,但这不太可能。它还需要仔细的文档,因为它不是事件通常的工作方式。

回答by ChrisW

I tend to add an event handler in a path that's executed once, for example in a constructor.

我倾向于在执行一次的路径中添加一个事件处理程序,例如在构造函数中。

回答by ?yvind Skaar

You can implement your own storage of the delgates, and check for uniqueness when adding them to the event. See EventOwner2 class below for an example. I don't know how this is doing performance wise, but than again, that is not always an issue.

您可以实现自己的 delegates 存储,并在将它们添加到事件时检查唯一性。有关示例,请参见下面的 EventOwner2 类。我不知道这在性能方面的表现如何,但同样,这并不总是一个问题。

using System;
using System.Collections.Generic;

namespace EventExperiment
{
    class Program
    {
        static void Main(string[] args)
        {
            IEventOwner e=new EventOwner2();
            Subscriber s=new Subscriber(e);
            e.RaiseSome();
            Console.ReadKey();
        }
    }

    /// <summary>
    /// A consumer class, subscribing twice to the event in it's constructor.
    /// </summary>
    public class Subscriber
    {
        public Subscriber(IEventOwner eventOwner)
        {
            eventOwner.SomeEvent += eventOwner_SomeEvent;
            eventOwner.SomeEvent += eventOwner_SomeEvent;
        }

        void eventOwner_SomeEvent(object sender, EventArgs e)
        {
            Console.WriteLine(DateTimeOffset.Now);
        }

    }

    /// <summary>
    /// This interface is not essensial to this point. it is just added for conveniance.
    /// </summary>
    public interface IEventOwner
    {
        event EventHandler<EventArgs> SomeEvent;
        void RaiseSome();
    }

    /// <summary>
    /// A traditional event. This is raised for each subscription.
    /// </summary>
    public class EventOwner1 : IEventOwner
    {
        public event EventHandler<EventArgs> SomeEvent = delegate { };
        public void RaiseSome()
        {
            SomeEvent(this,new EventArgs());
        }
    }
    /// <summary>
    /// A custom event. This is raised only once for each subscriber.
    /// </summary>
    public class EventOwner2 : IEventOwner
    {
        private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>();
        public event EventHandler<EventArgs> SomeEvent
        {
            add
            {
                lock (handlers)
                    if (handlers!=null&&!handlers.Contains(value))
                    {
                        handlers.Add(value);
                    }
            }
            remove
            {
                handlers.Remove(value);
            }
        }
        public void RaiseSome()
        {
            EventArgs args=new EventArgs();
            lock(handlers)
            foreach (EventHandler<EventArgs> handler in handlers)
            {
                handler(this,args);
            }
        }
    }
}

回答by jro

What is the access modifier of 'object'?

“对象”的访问修饰符是什么?

If it's private, you only need to worry about the containing object setting the event handler. If it's internal, you only need to worry about the containing assembly setting the event handler. If it's public, then it's wide-open.

如果它是私有的,您只需要担心包含对象设置事件处理程序。如果它是内部的,您只需要担心包含程序集设置事件处理程序。如果它是公开的,那么它是完全开放的。

If 'object' can be made private on the containing class, you can make your checks much more efficient by controlling event handler assignment in the local class.

如果可以在包含类上将“对象”设为私有,则可以通过控制本地类中的事件处理程序分配来提高检查效率。

If 'internal' or 'public' and uniqueness is a requirement, go with a wrapper class that hides 'object' and instead exposes a method for assigning an event handler with your checks behind it to ensure uniqueness.

如果“内部”或“公共”和唯一性是必需的,请使用隐藏“对象”的包装类,而是公开用于分配事件处理程序的方法,并在其后面进行检查以确保唯一性。