Событие выстрела с помощью Лямбды в C#

Я делаю этот вид вещи довольно часто:-

 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 часть кода и просто оставить отрывок в Лямбде?

44
задан AnthonyWJones 27 January 2010 в 21:58
поделиться

5 ответов

Это работает? Если так, то я говорю пойти на это. Для события одного выстрела, который выглядит довольно элегантным.

Что мне нравится ...

  • Если S - сборщик мусора, так что обработчик событий.
  • Отсоединительный код находится рядом с кодом прикрепления, что позволяет легко увидеть, что вы делаете.

Вы можете обобщить его, но я не правдивно уверен, как, потому что я не могу получить указатель на мероприятие.

1
ответ дан 26 November 2019 в 22:21
поделиться
-

Вы можете использовать отражение:

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!"));
8
ответ дан 26 November 2019 в 22:21
поделиться

Вы можете прикрепить постоянный обработчик события к событию. Затем обработчик событий вызывает «один обработчик событий 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);
    }
}
8
ответ дан 26 November 2019 в 22:21
поделиться

Если вы можете использовать реактивные расширения для .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();
        }
    }
}
6
ответ дан 26 November 2019 в 22:21
поделиться

Другой столкновенный пользователь Очень похожая проблема , и я считаю, что это решение в этой ните применяется здесь.

В частности, то, что у вас есть , экземпляр шаблона публикации / подписки, его очередь сообщений. Его достаточно легко, чтобы создать свое собственное очередь сообщений, используя очередь {EventHandler} , где вы набираете события, когда вы вызываете их.

Итак, вместо того, чтобы подключиться к обработчику событий, ваши события «One-Shot» должны предоставить метод, позволяющий клиентам добавлять функцию в очередь сообщений.

2
ответ дан 26 November 2019 в 22:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: