【问题标题】:How do I get the subscribers of an event?如何获得活动的订阅者?
【发布时间】:2010-10-09 00:41:48
【问题描述】:

我需要将一个事件的订阅者复制到另一个事件。我可以获取事件的订阅者(例如 MyEvent[0] 返回委托)吗?

如果这不可能,我会使用 add 访问器将代表添加到列表中。这会是最好的解决方案吗?

【问题讨论】:

    标签: c# events


    【解决方案1】:

    C# 事件/委托是多播的,因此委托本身是一个列表。在班级内,要获取个别呼叫者,您可以使用:

    if (field != null) 
    { 
        // or the event-name for field-like events
        // or your own event-type in place of EventHandler
        foreach(EventHandler subscriber in field.GetInvocationList())
        {
            // etc
        }
    }
    

    但是,要一次全部赋值,只需使用 += 或直接赋值:

    SomeType other = ...
    other.SomeEvent += localEvent;
    

    【讨论】:

    • 感谢您指出这一点。这最适合我自己的代码中的事件。
    • 非常感谢。我需要一个很好的解决方法来通过二进制序列化进行对象克隆而不享受订阅事件,否则我必须在数百个类中实现 ICloneable。
    【解决方案2】:

    如果事件是由另一个类发布的,您不能 - 至少,不可靠。虽然我们通常认为事件只是一个委托变量,但它实际上只是一对方法:添加和删除(或订阅和取消订阅)。

    如果是您自己的代码发布事件,这很容易 - 您可以让添加/删除访问器做任何您喜欢的事情。

    看看my article on events 看看是否对您有帮助。如果没有,请提供更多关于您想要做什么的详细信息,指定您可以修改哪些代码位以及您不能修改哪些代码。

    【讨论】:

      【解决方案3】:

      如果您需要检查外部类事件的订阅者:

      EventHandler e = typeof(ExternalClass)
          .GetField(nameof(ExternalClass.Event), BindingFlags.Instance | BindingFlags.NonPublic)
          .GetValue(instanceOfExternalClass) as EventHandler;
      if (e != null)
      {
          Delegate[] subscribers = e.GetInvocationList();
      }
      

      【讨论】:

        【解决方案4】:

        更新(感谢评论者):委托不变性意味着克隆对分配没有任何作用。

        当一个人写作时:

        myDelegate += AHandler
        

        一个全新的委托实例被创建并分配给myDelegate

        因此,如果没有 Clone 调用,下面的代码将完全一样地工作。


        MulticastDelegate(底层类型)有一个Clone 方法。

        为了能够访问底层委托,您可能需要避免使用 event 关键字生成的常用助手,并直接管理事物(自定义添加和删除访问器)。

        显示这个:

        public class Program {
            public delegate void MyDelegate(string name);
        
            public event MyDelegate EventOne;
        
            public void HandlerOne(string name) => Console.WriteLine($"This is handler one: {name}");
            public void HandlerTwo(string name) => Console.WriteLine($"This is handler two: {name}");
            public void HandlerThree(string name) => Console.WriteLine($"This is handler three: {name}");
        
            public void Run() {
                EventOne += HandlerOne;
                EventOne += HandlerTwo;
                Console.WriteLine("Before clone");
                EventOne("EventOne");
        
                MyDelegate eventTwo = (MyDelegate)EventOne.Clone();
                MyDelegate eventTwo = EventOne;
                Console.WriteLine("After clone copy");
                EventOne("EventOne");
                eventTwo("eventTwo");
        
                Console.WriteLine("Change event one to show it is different");
                EventOne += HandlerThree;
                EventOne("EventOne");
                eventTwo("eventTwo");
            }
            private static void Main(string[] args) => (new Program()).Run();
        }
        

        【讨论】:

        • 克隆本身相对不重要 - 委托是不可变的,因此您只需复制委托引用即可。
        • 除非像示例中那样,您想随后修改原件或独立复制。
        • Richard - 不,如果没有 Clone() 步骤,它会完全相同工作。
        • 另外,关于“能够访问底层委托” - C# 规范规定对于类似字段的事件(简单事件),然后在类内部对事件名称的任何引用正在与委托字段交谈。
        • 马克:当然。 (还没有足够的咖啡)。 (而且很容易忘记 += 是 .NET 不是其他语言的快捷方式)。
        猜你喜欢
        • 2017-07-22
        • 2015-09-27
        • 2016-06-10
        • 2014-05-06
        • 2018-02-06
        • 1970-01-01
        • 2021-08-16
        • 1970-01-01
        • 2012-09-24
        相关资源
        最近更新 更多