C#: Triggering an Event When an Object Is Added to a Queue

C#: Triggering an Event when an object is added to a Queue

If you mean the non-generic Queue class, then you can just override Enqueue:

public override void Enqueue(object obj)
{
base.Enqueue(obj);
OnChanged(EventArgs.Empty);
}

However, if you mean the generic Queue<T> class, then note that there is no suitable virtual method to override. You might do better to encapsulate the queue with your own class:

(** important edit: removed base-class!!! **)

class Foo<T>
{
private readonly Queue<T> queue = new Queue<T>();
public event EventHandler Changed;
protected virtual void OnChanged()
{
if (Changed != null) Changed(this, EventArgs.Empty);
}
public virtual void Enqueue(T item)
{
queue.Enqueue(item);
OnChanged();
}
public int Count { get { return queue.Count; } }

public virtual T Dequeue()
{
T item = queue.Dequeue();
OnChanged();
return item;
}
}

However, looking at your code, it seems possible that you are using multiple threads here. If that is the case, consider a threaded queue instead.

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

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}");
}
}

Event triggering after subscription

You can customize the adding and removing of event handlers in the code of the event itself by using the add and remove statements:

private EventHandler onMyEvent;
public event MyEventHandler MyEvent
{
add
{
// run when event handler is added ( += )
onMyEvent = (MyEventHandler)Delegate.Combine(onMyEvent, value);

// Add additional, custom logic here...
}

remove
{
// run when event handler is removed ( -= )
onMyEvent = (MyEventHandler)Delegate.Remove(onMyEvent, value);
}
}

Here you can add your own code to trigger actions upon adding or removing your event handler.

C# event subscriber queuing

This is pretty easy to test. Here's a program I set up to show what happens:

class Program
{
static void Main(string[] args)
{
Invoker invoker = new Invoker();
Consumer consumer = new Consumer(invoker);
invoker.RunEvents();
}
}

class Invoker
{
public delegate void SomeEventHandler();

public event SomeEventHandler SomeEvent;

public void RunEvents()
{
while (true)
SomeEvent.Invoke();
}
}

class Consumer
{
public Consumer(Invoker invoker)
{
invoker.SomeEvent += HandleSomeEvent;
}

private void HandleSomeEvent()
{
Console.WriteLine("Handling event");
Thread.Sleep(500);
}
}

What you'll find is that the "Handling event" message is only output to console once every 500ms even though the Invoker() is trying to trigger the event as rapidly as possible.

This is because the event handler is running on the same thread as the invoker. There's no queueing up or starting a new thread. The invoker simply can't invoke the event more quickly than the handler handles the event because they're on the same thread.

How to add to a queue from FileSystemWatcher Onchanged Event and trigger different actions based on which file is changed

The FileSystemEventArgs which is passed in the OnChanged handler has the following properties you can use to determine specifics of the action.

ChangeType Gets the type of directory event that occurred.

FullPath Gets the fully qualified path of the affected file or directory.

Name Gets the name of the affected file or directory

With those you will know which file changed.
How you link the file names to actions can be done in any number of ways.
You could create a simple function that links file name to a method, however you would need to compile if you needed to change the file name or action to execute.



Related Topics



Leave a reply



Submit