Я делаю этот вид вещи довольно часто:-
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;
В основном я только хочу присоединить обработчик событий для прислушиваний к выстрелу того от события, я больше не хочу остаться присоединенным после этого. Довольно часто это "snippert кода" является всего одной строкой.
Мой ум идет немного оцепенелый, я уверен, что должно быть что-то, что я могу сделать так, я не должен повторять все эти издержки. Примите во внимание это EventHandler
может быть EventHandler<T>
.
Какие-либо идеи, как я могу убрать repeative часть кода и просто оставить отрывок в Лямбде?
Это работает? Если так, то я говорю пойти на это. Для события одного выстрела, который выглядит довольно элегантным.
Что мне нравится ...
Вы можете обобщить его, но я не правдивно уверен, как, потому что я не могу получить указатель на мероприятие.
Вы можете использовать отражение:
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!"));
Вы можете прикрепить постоянный обработчик события к событию. Затем обработчик событий вызывает «один обработчик событий Shot», которые добавляются в внутреннюю очередь:
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();
код:
class OneShotHandlerQueue<TEventArgs> where TEventArgs : EventArgs {
private ConcurrentQueue<EventHandler<TEventArgs>> queue;
public OneShotHandlerQueue() {
this.queue = new ConcurrentQueue<EventHandler<TEventArgs>>();
}
public void Handle(object sender, TEventArgs e) {
EventHandler<TEventArgs> handler;
if (this.queue.TryDequeue(out handler) && (handler != null))
handler(sender, e);
}
public void Add(EventHandler<TEventArgs> handler) {
this.queue.Enqueue(handler);
}
}
Класс тестирования:
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);
}
}
Если вы можете использовать реактивные расширения для .NET , вы можете упростить это.
Вы можете сделать , наблюдаемый из события , и только слушаю первый элемент, используя .take (1)
, чтобы сделать свой небольшой фрагмент кода. Это превращает весь этот процесс в пару строк кода.
Отредактируйте: для того, чтобы продемонстрировать, я сделал полный пример программы (я вставлю ниже).
Я переместил наблюдаемое создание и подписку в метод ( рубль
). Это позволяет вам делать то, что вы пытаетесь с одним вызовом метода. Для демонстрации я сделал класс с двумя свойствами, который реализует INOTIFYPROPERTYCHANTED, а также прослушиваю к мероприятию первого события, изменившееся событие, написание на консоль, когда это происходит.
Это требует вашего кода и меняет его на:
HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent", e => {
// Small snippet of code here
});
Обратите внимание, что вся подписка / отрицательная проверка происходит автоматически для вас за кулисами. Там нет необходимости обрабатывать вручную подписку вручную - просто подпишитесь на наблюдаемые, а RX заботится об этом для вас.
При запуске этот код печатает:
Setup...
Setting first property...
**** Prop2 Changed! /new val
Setting second property...
Setting first property again.
Press ENTER to continue...
Вы получаете только один триггер с одним событием.
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();
}
}
}
Другой столкновенный пользователь Очень похожая проблема , и я считаю, что это решение в этой ните применяется здесь.
В частности, то, что у вас есть , экземпляр шаблона публикации / подписки, его очередь сообщений. Его достаточно легко, чтобы создать свое собственное очередь сообщений, используя очередь {EventHandler}
, где вы набираете события, когда вы вызываете их.
Итак, вместо того, чтобы подключиться к обработчику событий, ваши события «One-Shot» должны предоставить метод, позволяющий клиентам добавлять функцию в очередь сообщений.