【问题标题】:Forwarding events in C#在 C# 中转发事件
【发布时间】:2023-03-09 01:06:01
【问题描述】:

我正在使用一个在 C# 中转发事件的类。我想知道是否有办法做 它需要更少的代码开销。

这是我到目前为止的一个例子。

class A
{
   public event EventType EventA;
}

class B
{
   A m_A = new A();
   public event EventType EventB;

   public B()
   {
      m_A.EventA += OnEventA;
   }

   public void OnEventA()
   {
      if( EventB )
      {
         EventB();
      }
   }
}

A 类引发了原始事件。 B 类将其作为 EventB 转发(本质上是相同的事件)。 A 类对其他模块是隐藏的,因此它们不能直接订阅 EventA。

我要做的是减少 B 类中用于转发事件的代码开销,因为通常 B 类中的事件没有真正的处理。此外,我将有几个不同的事件,因此需要编写一个B 类中的许多 OnEvent() 方法仅用于转发事件。

是否有可能以某种方式自动将 EventA 链接到 EventB,所以我有这样的东西:

class B
{
   A m_A = new A();
   public event EventType EventB;

   public B()
   {
      m_A.EventA += EventB; // EventA automatically raises EventB.
   }
}

顺便说一句,我正在使用 C# 2.0 编译器。

【问题讨论】:

  • 很好的问题,特别是因为您不应该忘记取消订阅 EventA,否则可能会出现内存泄漏(您的对象 B 将与 A 一样保留在内存中,即使不再需要它)。

标签: c# events event-handling c#-2.0


【解决方案1】:

这是我想出的:

public interface IExampleConnection
{
    event ReceivedDataEventHandler ReceivedData;
}

public class ConnectionProxy: IExampleConnection
{
    private IExampleConnection _innerConnection;

    // dictionary to store the original event handler and the closure around it with our own handling logic
    private IDictionary<ReceivedDataEventHandler, ReceivedDataEventHandler> _receivedData = new Dictionary<ReceivedDataEventHandler, ReceivedDataEventHandler>();

    // helps protect access to the dictionary containing the event handlers 
    private object objectLock = new object();

    public ConnectionProxy(IExampleConnection innerConnection)
    {
        _innerConnection = innerConnection;
    }
    
    public event ReceivedDataEventHandler ReceivedData
    {
        add
        {
            lock (objectLock)
            {
                // use the original event handler `value` as a key in the dictionary
                // our custom handler becomes the value
                _receivedData.Add(value, (sender, args) =>
                {
                    // insert logic that you want to run before the original event handler

                    // call the original event handler
                    value(sender, args);

                    // insert logic that you want to run after the original event handler finishes

                });
                // add our handler to the dictionary by using the value as the key
                _innerConnection.ReceivedData += _receivedData[value];
            }
        }
        remove
        {
            lock (objectLock)
            {
                // use the incoming event handler `value` to lookup our wrapper around it
                _innerConnection.ReceivedData -= _receivedData[value];
            }
        }
    }
}

这比我通常想要的要多一些代码。我怀疑有办法让它更简洁,但它对我的目的来说已经足够好了。

【讨论】:

    【解决方案2】:

    IMO,您的原始代码(或多或少)是正确的。特别是,它允许您提供正确的sender(对于那些认为他们正在订阅B 上的事件的人来说,这应该是B 实例)。

    如果事件没有被订阅,有一些技巧可以减少运行时的开销,但这会增加更多代码:

    class B {
       A m_A = new A();
       private EventType eventB;
       public event EventType EventB {
           add { // only subscribe when we have a subscriber ourselves
               bool first = eventB == null;
               eventB += value;
               if(first && eventB != null) m_A.EventA += OnEventB;
           }
           remove { // unsubscribe if we have no more subscribers
               eventB -= value;
               if(eventB == null) m_A.EventA -= OnEventB;
           }
       }
    
       protected void OnEventB(object sender, EventArgsType args) {
          eventB?.Invoke(this, args); // note "this", not "sender"
    
       }
    }
    

    【讨论】:

      【解决方案3】:

      绝对:

      class B
      {
          private A m_a = new A();
      
          public event EventType EventB
          {
              add { m_a.EventA += value; }
              remove { m_a.EventA -= value; }
          }
      }
      

      换句话说,Eve​​ntB 订阅/取消订阅代码只是将订阅/取消订阅请求传递给 EventA。

      请注意,这不允许您为订阅了 EventB 的订阅者引发事件只是。这就像将某人的地址直接传递给大众营销公司,而您原来的方式更像是自己订阅大众营销公司,并允许人们要求您将邮件的副本发送给他们。

      【讨论】:

      • 这确实意味着B.EventB 的订阅者收到了错误的发件人(A 而不是B)——这可能很重要...
      • 对,对...当然取决于您认为“错误”的内容。至少可能值得记录。
      • 如果你不关心发件人那么这个模式很流畅 imo
      猜你喜欢
      • 1970-01-01
      • 2016-09-28
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-01
      • 1970-01-01
      相关资源
      最近更新 更多