user77232
user77232

Reputation: 243

How do I create a queue of event delegates so you can raise them later?

Is it possible to add an event (or delegate) to a list so that I can raise the even at a later point; like being able to cache the raise event to put it off to a later time (like buffering the events).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelegateQueue {
    class Program {

        delegate void SomeTimeLaterDelegate(int myValue);
        static event SomeTimeLaterDelegate SomeTimeLater;
        static void Main(string[] args) {
            SomeTimeLater += new SomeTimeLaterDelegate(AnswerAnotherDay);
            SomeTimeLater += new SomeTimeLaterDelegate(ItIsPossible);
            SomeTimeLater += new SomeTimeLaterDelegate(HenceTheNeed);

            List<Delegate> delegates = new List<Delegate>();

            SomeTimeLater.Invoke(100); // <== I need to trap this call in a list that I can invoke later.
            ///both the function call and the value that I'm passing.
            ///the problem is that there are lots of different events and they must be invoked in the order
            ///that they occoured (i.e. no skipping in the buffer)

        }

        static void AnswerAnotherDay(int howManyDays) {
            Console.WriteLine($"I did this {howManyDays}, later. Hurray for procrastination!");
        }

        static void ItIsPossible(int numberOfPossibilities) {
        ///that there is in invocation list for the event
        ///which means that I would have to call each function in that list and pass them the value

        }

        static void HenceTheNeed(int needs) {
        ///hence the need to queue the Invocation of the event to a later point
        }
    }
}

It is possible that there are multiple subscribers to the event, hence the need to buffer from the publisher's side as opposed to the subscriber's side.

Upvotes: 1

Views: 1164

Answers (2)

Theodor Zoulias
Theodor Zoulias

Reputation: 43554

Here is my take on the challenge:

delegate void SomeTimeLaterDelegate(int myValue);
static event SomeTimeLaterDelegate SomeTimeLater;

static Queue<int> SomeTimeLaterQueue = new Queue<int>(); // Here we store the event arguments

static async Task Main(string[] args)
{
    SomeTimeLater += new SomeTimeLaterDelegate(AnswerAnotherDay); // Subscribe to the event
    await Task.Delay(100);
    OnSomeTimeLaterDefered(10); // Raise an defered event
    await Task.Delay(100);
    OnSomeTimeLaterDefered(20); // Raise another defered event
    await Task.Delay(100);
    OnSomeTimeLaterDefered(30); // Raise a third defered event
    await Task.Delay(100);
    OnSomeTimeLaterFlush(); // Time to raise the queued events for real!
}

static void OnSomeTimeLaterDefered(int value)
{
    SomeTimeLaterQueue.Enqueue(value);
}
static void OnSomeTimeLaterFlush()
{
    while (SomeTimeLaterQueue.Count > 0)
    {
        SomeTimeLater?.Invoke(SomeTimeLaterQueue.Dequeue());
    }
}

static void AnswerAnotherDay(int howManyDays)
{
    Console.WriteLine($"I did this {howManyDays}, later. Hurray for procrastination!");
}

Output:

I did this 10, later. Hurray for procrastination!
I did this 20, later. Hurray for procrastination!
I did this 30, later. Hurray for procrastination!

Upvotes: 0

Scott Hannen
Scott Hannen

Reputation: 29222

Here's a literal example of what you're describing:

class Program
{
    delegate void SomeTimeLaterDelegate(int myValue);
    delegate void SayHelloDelegate(string name);

    static event SomeTimeLaterDelegate SomeTimeLaterEvent;
    static event SayHelloDelegate SayHelloLaterEvent;
    static void Main(string[] args)
    {
        SomeTimeLaterEvent += new SomeTimeLaterDelegate(AnswerAnotherDay);
        SayHelloLaterEvent += new SayHelloDelegate(SayHello);

        var eventsToRaise = new Queue<Action>();
        eventsToRaise.Enqueue(() => SomeTimeLaterEvent.Invoke(100));
        eventsToRaise.Enqueue(() => SayHelloLaterEvent.Invoke("Bob"));
        eventsToRaise.Enqueue(() => SomeTimeLaterEvent.Invoke(200));
        eventsToRaise.Enqueue(() => SayHelloLaterEvent.Invoke("John"));

        while (eventsToRaise.Any())
        {
            var eventToRaise = eventsToRaise.Dequeue();
            eventToRaise();
            //or eventToRaise.Invoke();
        }

        Console.ReadLine();
    }

    static void AnswerAnotherDay(int howManyDays)
    {
        Console.WriteLine($"I did this {howManyDays}, later. Hurray for procrastination!");
    }

    static void SayHello(string name)
    {
        Console.WriteLine($"Hello, {name}");
    }
}

Instead of a Queue<Delegate> this is a Queue<Action>. An Action represents invoking a method without passing any parameters or receiving a return value.

That might sound counter-intuitive because you are passing parameters. But you're not passing parameters to the actions. You're passing parameters from within the body of the actions.

This may or may not help:

When we declare

Action someAction = () => AnswerAnotherDay(5);

It's like declaring a method that looks like this:

void MyMethod()
{
    AnswerAnotherDay(5);
}

The method calls another method and passes an argument. But MyMethod itself doesn't receive any arguments. When we declare a method inline like that it doesn't have a name, so it's also called an anonymous method.

We could also declare an action that takes an argument, like this:

Action<string> action = (s) => SayHelloLater(s);
action("Bob");

The reason why I demonstrated using Action with no parameters is because you said that the queue might need to contain different types of events with different parameters.

If I was going to invoke the same method over and over again with different arguments then it would probably make more sense to put the arguments in the queue. Then each time we take arguments out of the queue we could raise the event with the next arguments.

Here's a possible simplification: Maybe you're thinking of a scenario where you need to raise events, but in this example we can do the exact same thing without events, and it's a little bit simpler.

Instead of defining delegates and events, adding certain methods as event handlers, and then creating actions that raise the events, we can just create actions that call the methods.

class Program
{
    static void Main(string[] args)
    {
        var eventsToRaise = new Queue<Action>();
        eventsToRaise.Enqueue(() => AnswerAnotherDay(100));
        eventsToRaise.Enqueue(() => SayHello("Bob"));
        eventsToRaise.Enqueue(() => AnswerAnotherDay(200));
        eventsToRaise.Enqueue(() => SayHello("John"));

        while (eventsToRaise.Any())
        {
            var eventToRaise = eventsToRaise.Dequeue();
            eventToRaise();
            //or eventToRaise.Invoke();
        }

        Console.ReadLine();
    }

    static void AnswerAnotherDay(int howManyDays)
    {
        Console.WriteLine($"I did this {howManyDays}, later. Hurray for procrastination!");
    }

    static void SayHello(string name)
    {
        Console.WriteLine($"Hello, {name}");
    }
}

Upvotes: 2

Related Questions