C# C语言中使用Lambda的一次性事件#

C# C语言中使用Lambda的一次性事件#,c#,events,lambda,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 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

你知道我如何整理代码中重复的部分,并将代码片段保留在Lambda中吗?

如果你可以使用,你可以简化这个过程

您可以使用
,只监听第一个元素。使用(1)
,执行您的小代码片段。这将整个过程转化为几行代码


编辑:为了演示,我制作了一个完整的示例程序(我将在下面粘贴)

我将可观察的创建和订阅移动到一个方法中(
HandleOneShot
)。这使您可以通过一个方法调用来完成所尝试的操作。为了演示,我创建了一个具有两个属性的类,它们实现了INotifyPropertyChanged,我正在侦听firstproperty changed事件,并在事件发生时写入控制台

这将获取您的代码,并将其更改为:

HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent",  e => { 
                    // Small snippet of code here
                }); 
你只有一次,一次触发你的事件

namespace ConsoleApplication1
{
    using System;
    using System.ComponentModel;
    using System.Linq;

    class Test : INotifyPropertyChanged
    {
        private string prop2;
        private string prop;
        public string Prop
        {
            get {
                return prop;
            }
            set
            {
                if (prop != value)
                {
                    prop = value;
                    if (PropertyChanged!=null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Prop"));
                }
            }
        }

        public string Prop2
        {
            get
            {
                return prop2;
            }
            set
            {
                if (prop2 != value)
                {
                    prop2 = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Prop2"));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }


    class Program
    {
        static void HandleOneShot<TEventArgs>(object target, string eventName, Action<TEventArgs> action)  where TEventArgs : EventArgs
        {
            var obsEvent = Observable.FromEvent<TEventArgs>(target, eventName).Take(1);
            obsEvent.Subscribe(a => action(a.EventArgs));
        }

        static void Main(string[] args)
        {
            Test test = new Test();

            Console.WriteLine("Setup...");
            HandleOneShot<PropertyChangedEventArgs>(
                test, 
                "PropertyChanged", 
                e =>
                    {
                        Console.WriteLine(" **** {0} Changed! {1}/{2}!", e.PropertyName, test.Prop, test.Prop2);
                    });

            Console.WriteLine("Setting first property...");
            test.Prop2 = "new value";
            Console.WriteLine("Setting second property...");
            test.Prop = "second value";
            Console.WriteLine("Setting first property again...");
            test.Prop2 = "other value";

            Console.WriteLine("Press ENTER to continue...");
            Console.ReadLine();
        }
    }
}
命名空间控制台应用程序1
{
使用制度;
使用系统组件模型;
使用System.Linq;
类测试:INotifyPropertyChanged
{
私有字符串prop2;
私人弦道具;
公共字符串道具
{
得到{
返回道具;
}
设置
{
if(prop!=值)
{
道具=价值;
if(PropertyChanged!=null)
房地产变更(这是指新的房地产变更开发项目(“Prop”);
}
}
}
公共字符串Prop2
{
得到
{
返回prop2;
}
设置
{
如果(prop2!=值)
{
prop2=值;
if(PropertyChanged!=null)
房地产变更(这是指新的房地产变更数据(“Prop2”);
}
}
}
公共事件属性更改事件处理程序属性更改;
}
班级计划
{
静态void HandleOneShot(对象目标、字符串eventName、动作动作),其中TEventArgs:EventArgs
{
var obsEvent=Observable.FromEvent(目标,事件名称).Take(1);
订阅(a=>action(a.EventArgs));
}
静态void Main(字符串[]参数)
{
测试=新测试();
Console.WriteLine(“设置…”);
汉德莱昂内绍特(
测试,
“财产变更”,
e=>
{
WriteLine(“**{0}已更改!{1}/{2}!”,e.PropertyName,test.Prop,test.Prop2);
});
WriteLine(“设置第一个属性…”);
test.Prop2=“新值”;
WriteLine(“设置第二个属性…”);
test.Prop=“第二个值”;
WriteLine(“再次设置第一个属性…”);
test.Prop2=“其他值”;
Console.WriteLine(“按ENTER键继续…”);
Console.ReadLine();
}
}
}

它有效吗?如果是的话,那我就说努力吧。对于一个看起来相当优雅的一次性活动

我喜欢的是

  • 如果s是垃圾收集的,那么事件处理程序也是垃圾收集的
  • 分离代码就在附加代码的旁边,这样可以很容易地看到您正在做什么

您可能可以对其进行泛化,但我不完全确定如何进行泛化,因为我似乎无法获取指向事件的指针。

您可以为该事件附加一个永久事件处理程序。然后,事件处理程序调用添加到内部队列的“一次性事件处理程序”:

OneShotHandlerQueue<EventArgs> queue = new OneShotHandlerQueue<EventArgs>();

Test test = new Test();

// attach permanent event handler
test.Done += queue.Handle;

// add a "one shot" event handler
queue.Add((sender, e) => Console.WriteLine(e));
test.Start();

// add another "one shot" event handler
queue.Add((sender, e) => Console.WriteLine(e));
test.Start();
另一个用户遇到,我相信该线程中的解决方案适用于这里

特别是,您拥有的不是发布/订阅模式的实例,而是消息队列。使用
队列{EventHandler}
创建您自己的消息队列非常简单,在这里您可以在调用事件时将其出列


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

您可以使用反射:

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) => {
      eventInfo.RemoveEventHandler(eventSource, internalHandler);
      handler(src, args);
    };
    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) => {
      eventInfo.RemoveEventHandler(eventSource, internalHandler);
      handler(src, args);
    };
    eventInfo.AddEventHandler(eventSource, internalHandler);
  }

}
公共静态类侦听器{
公共静态void listence(此对象eventSource、字符串eventName、EventHandler){
var eventInfo=eventSource.GetType().GetEvent(eventName);
EventHandler internalHandler=null;
internalHandler=(src,args)=>{
RemoveEventHandler(eventSource,internalHandler);
处理程序(src,args);
};
AddEventHandler(eventSource,internalHandler);
}
公共静态void listence(此对象eventSource、字符串eventName、EventHandler处理程序),其中TEventArgs:EventArgs{
var eventInfo=eventSource.GetType().GetEvent(eventName);
EventHandler internalHandler=null;
internalHandler=(src,args)=>{
RemoveEventHandler(eventSource,internalHandler);
处理程序(src,args);
};
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!"));
variableOfSomeType.listence(“SomeEvent”,
(s,args)=>Console.WriteLine(“我应该只打印一次!”);
variableOfSomeType.Listence(“SomeOtherEvent”,
(s,args)=>Console.WriteLine(“我应该只打印一次!”);

就我个人而言,我只是为我正在处理的任何类型的事件创建了一个专门的扩展方法
class Test {
    public event EventHandler Done;
    public void Start() {
        this.OnDone(new EventArgs());
    }
    protected virtual void OnDone(EventArgs e) {
        EventHandler handler = this.Done;
        if (handler != null)
            handler(this, e);
    }
}
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) => {
      eventInfo.RemoveEventHandler(eventSource, internalHandler);
      handler(src, args);
    };
    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) => {
      eventInfo.RemoveEventHandler(eventSource, internalHandler);
      handler(src, args);
    };
    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!"));
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;
        }
    }
}
await variableOfSomeSort.SomeMethodAsync();
//small snippet of code here
    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);
        }
    }