c# 在C#中使用Lambda的一次性事件



4 Answers

你可以使用反射:

public static class Listener {

  public static void ListenOnce(this object eventSource, string eventName, EventHandler handler) {
    var eventInfo = eventSource.GetType().GetEvent(eventName);
    EventHandler internalHandler = null;
    internalHandler = (src, args) => {
      handler(src, args);
      eventInfo.RemoveEventHandler(eventSource, internalHandler);
    };
    eventInfo.AddEventHandler(eventSource, internalHandler);
  }

  public static void ListenOnce<TEventArgs>(this object eventSource, string eventName, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs {
    var eventInfo = eventSource.GetType().GetEvent(eventName);
    EventHandler<TEventArgs> internalHandler = null;
    internalHandler = (src, args) => {
      handler(src, args);
      eventInfo.RemoveEventHandler(eventSource, internalHandler);
    };
    eventInfo.AddEventHandler(eventSource, internalHandler);
  }

}

像这样使用它:

variableOfSomeType.ListenOnce("SomeEvent", 
  (s, args) => Console.WriteLine("I should print only once!"));

variableOfSomeType.ListenOnce<InterestingEventArgs>("SomeOtherEvent", 
  (s, args) => Console.WriteLine("I should print only once!"));
c# events lambda

我发现自己经常这样做: -

 EventHandler eh = null;  //can't assign lambda directly since it uses eh
 eh = (s, args) =>
 {
     //small snippet of code here

     ((SomeType)s).SomeEvent -= eh;
 }
 variableOfSomeType.SomeEvent += eh;

基本上我只想附加一个事件处理程序来监听事件中的一个镜头,之后我不想再保持联系。 通常,“代码片段”只是一行。

我的思绪有点麻木,我确信必须有一些我可以做的事情,所以我不需要重复所有这些开销。 请记住, EventHandler可能是EventHandler<T>

任何想法如何我可以整理代码的重复部分,只是将片段留在Lambda中?




另一个用户遇到了一个非常类似的问题 ,我相信该线程中的解决方案适用于此处。

特别是,您拥有的不是发布/订阅模式的实例,它是一个消息队列。 使用Queue{EventHandler}创建自己的消息队列非常容易,您可以在调用事件时将事件Queue{EventHandler}

因此,不是挂钩到事件处理程序,而是“一次性”事件应该公开一种允许客户端向消息队列添加函数的方法。




就个人而言,我只是为我正在处理的事件创建一个专门的扩展方法。

这是我现在使用的基本版本:

namespace MyLibrary
{
    public static class FrameworkElementExtensions
    {
        public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler)
        {
            RoutedEventHandler wrapperHandler = null;
            wrapperHandler = delegate
            {
                el.Loaded -= wrapperHandler;

                handler(el, null);
            };
            el.Loaded += wrapperHandler;
        }
    }
}

我认为这是最佳解决方案的原因是因为您通常不需要只处理一次事件。 您还经常需要检查事件是否已经通过...例如,以下是上述扩展方法的另一个版本,它使用附加属性来检查元素是否已经加载,在这种情况下它只调用给定的处理程序马上:

namespace MyLibraryOrApplication
{
    public static class FrameworkElementExtensions
    {
        public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler)
        {
            if ((bool)el.GetValue(View.IsLoadedProperty))
            {
                // el already loaded, call the handler now.
                handler(el, null);
                return;
            }
            // el not loaded yet. Attach a wrapper handler that can be removed upon execution.
            RoutedEventHandler wrapperHandler = null;
            wrapperHandler = delegate
            {
                el.Loaded -= wrapperHandler;
                el.SetValue(View.IsLoadedProperty, true);

                handler(el, null);
            };
            el.Loaded += wrapperHandler;
        }
    }
}



为什么不使用事件中内置的委托堆栈? 就像是...

    private void OnCheckedIn(object sender, Session e)
    {
        EventHandler<Session> nextInLine = null; 
        lock (_syncLock)
        {
            if (SessionCheckedIn != null)
            {
                nextInLine = (EventHandler<Session>)SessionCheckedIn.GetInvocationList()[0];
                SessionCheckedIn -= nextInLine;
            }
        }

        if ( nextInLine != null )
        {
            nextInLine(this, e);
        }
    }





Related