Raise event through a general method - c#

I'm trying to create a reusable method for a more complicated event execution. I can't get it to compile or run with framework events that don't follow the EventHandler<Type> pattern. I would like to avoid reflection if possible as it will be a heavily used event.
I've created a test console app below which illustrates the problem:
using System;
using System.Collections.Specialized;
namespace CallEventsViaMethod
{
public class TestEventArgs : EventArgs { }
class Program
{
static void Main(string[] args)
{
MyProgram program = new MyProgram();
program.Go();
Console.ReadKey(false);
}
}
public class MyProgram
{
public event EventHandler<TestEventArgs> TestEvent;
public event NotifyCollectionChangedEventHandler CollectionChangedEvent;
public void Go()
{
TestEvent += new EventHandler<TestEventArgs>(MyProgram_TestEvent);
CollectionChangedEvent += new NotifyCollectionChangedEventHandler(MyProgram_CollectionChangedEvent);
// Want a reusable method I can use to conditionally execute any event
GeneralEventExecutor.Execute<TestEventArgs>(TestEvent, new Object(), new TestEventArgs());
GeneralEventExecutor.Execute<NotifyCollectionChangedEventArgs>(TestEvent, new Object(), new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
void MyProgram_TestEvent(object arg1, TestEventArgs arg2)
{
Console.WriteLine("Custom event ran");
}
void MyProgram_CollectionChangedEvent(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("NotifyCollectionChangedEventHandler event ran");
}
}
public static class GeneralEventExecutor
{
public static void Execute<T>(EventHandler<T> eventToRaise, object sender, T eventArgs) where T : EventArgs
{
if (eventToRaise == null)
return;
Delegate[] registeredEventHandlers = eventToRaise.GetInvocationList();
foreach (EventHandler<T> eventHandler in registeredEventHandlers)
{
object target = eventHandler.Target; // Need access to the Target property
// * Code deciding if should invoke the event handler *
eventHandler.Invoke(sender, eventArgs);
}
}
}
}
Error messages are:
error CS1502: The best overloaded method match for
'CallEventsViaMethod.GeneralEventExecutor.Execute(System.EventHandler,
object,
System.Collections.Specialized.NotifyCollectionChangedEventArgs)' has
some invalid arguments
error CS1503: Argument 1: cannot convert from
'System.EventHandler' to
'System.EventHandler'
I understand why I'm getting the error, but can't figure out a way round it.

Replace your your generic Execute<T> as below
public static void Execute<T>(Delegate eventToRaise, object sender, T eventArgs) where T:EventArgs
{
if (eventToRaise == null)
return;
Delegate[] registeredEventHandlers = eventToRaise.GetInvocationList();
foreach (Delegate eventHandler in registeredEventHandlers)
{
object target = eventHandler.Target; // Need access to the Target property for conditions
// * Code deciding if should invoke the event handler *
eventHandler.DynamicInvoke(sender, eventArgs);
}
}

Related

EventHandler upcasting/downcasting

I've a library that defines ConfigurationChanged event. It used to use EventArgs, but I want to extend it to MyEventArgs, but don't want to bring BC break to customers. They shall still be able to have EventArgs signature to consume the event args.
It works fine as long as they assign their handling method directly. But If some of them pass EventHandler<EventArgs> around, then it won't assign with
Cannot implicitly convert type 'System.EventHandler<System.EventArgs>' to 'System.EventHandler<MyEventArgs>'.
Code snippet
public class Test
{
private static event EventHandler<MyEventArgs> ConfigurationChanged;
public Test(EventHandler<EventArgs> eventHandler)
{
ConfigurationChanged += eventHandler; // Cannot implicitly convert
ConfigurationChanged += OnConfigurationChanged; // Works
}
private static void OnConfigurationChanged(object sender, EventArgs e)
{
}
}
public class MyEventArgs : EventArgs
{
}
Would you have any workaround/best practice around that or there is no way to obey breaking change?
Changing the type of the event is a binary breaking change whatever you do. What you've described is trying to make it not a source breaking change.
What you can do is add the more specific event as a separate event, and proxy event subscription by creating a new EventHandler<MyEventArgs> from the handler that's passed to the add/remove parts. Fortunately, delegate equality still works in that situation, so unsubscribing does the right thing automatically:
using System;
public class MyEventArgs : EventArgs
{
}
public class Test
{
public event EventHandler<EventArgs> GeneralEvent
{
add => SpecificEvent += new EventHandler<MyEventArgs>(value);
remove => SpecificEvent -= new EventHandler<MyEventArgs>(value);
}
public event EventHandler<MyEventArgs> SpecificEvent;
private void OnEvent(MyEventArgs args)
{
SpecificEvent?.Invoke(this, args);
}
public static void Main()
{
var test = new Test();
EventHandler<EventArgs> generalHandler = (sender, args) => Console.WriteLine("General");
EventHandler<MyEventArgs> specificHandler = (sender, args) => Console.WriteLine("Specific");
test.GeneralEvent += generalHandler;
test.SpecificEvent += specificHandler;
Console.WriteLine("Raising event with both subscribed");
test.OnEvent(new MyEventArgs());
test.GeneralEvent -= generalHandler;
test.SpecificEvent -= specificHandler;
Console.WriteLine("Raising event with both unsubscribed");
test.OnEvent(new MyEventArgs());
}
}

C# - Why is there an error using EventHandler<T>?

Doing a Pluralsight video and can't find out why it's wrong.
Error Message on this point:
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);
Error Message:
Cannot implicity convert to type
'System.EventHandler<AB_Events.WorkPerformedEventArgs>' to
'AB_Events.WorkPerformedHandler'
Snippets
public delegate int WorkPerformedHandler(object sender,WorkPerformedEventArgs e);
public class Worker
{
public event WorkPerformedHandler WorkPerformed;
protected virtual void OnWorkPerformed(int hours, WorkType workType)
{
var del = WorkPerformed as WorkPerformedHandler;
if (del != null)
{
del(this, new WorkPerformedEventArgs(hours, workType));
}
}
}
And
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);
Console.ReadKey();
}
public static void worker_WorkPerformed(object sender, WorkPerformedEventArgs e)
{
throw new NotImplementedException();
}
}
Although methods and anonymous functions are implicitly convertible to a delegate type that matches their signature, a delegate instance is not implicitly convertible to another delegate type.
You need to use either EventHandler<TEventArgs> or your WorkPerformedHandler, but not both:
public class Worker
{
public event WorkPerformedHandler WorkPerformed;
//...
}
Or:
public class Worker
{
public event EventHandler<WorkPerformedEventArgs> WorkPerformed;
//...
}
Also, because worker_WorkPerformed can be implicitly converted to either type, the most concise syntax would be this:
worker.WorkPerformed += worker_WorkPerformed; // Implicit conversion
If you do go with your WorkPerformedHandler delegate, make sure you change the return type to void as per the signature of worker_WorkPerformed:
public delegate void WorkPerformedHandler(object sender, WorkPerformedEventArgs e);
It is generally not advisable to return from an event handler anyway, as there can be multiple subscribers.
what i have done is to replace WorkPerformedHandler by EventHandler<WorkPerformedEventArgs>
public delegate int WorkPerformedHandler(object sender, WorkPerformedEventArgs e);
public class Worker
{
public event EventHandler<WorkPerformedEventArgs> WorkPerformed;
protected virtual void OnWorkPerformed(WorkPerformedEventArgs e)
{
WorkPerformed?.Invoke(this, e);
}
}
event subscription :
var worker = new Worker();
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);

Delegate to an instance method cannot have null 'this'. using EventHandler with an DLL

Hi what I've done is writing an event handler within an DLL that passing over an json string as an argument. So far so good but during runtime I'm receiving the following error:
Delegate to an instance method cannot have null 'this'.
Within my DLL the code is looking like this:
public delegate void JsonEventHandler(Object sender, JsonEventArgs e);
private void stuff()
{
//some more code
JsonEventArgs eva = new JsonEventArgs();
eva.msg = jsonMsg;
initializeMeasure(this, eva);
}
public event JsonEventHandler initializeMeasure;
public class JsonEventArgs : EventArgs
{
public EmoJson msg { get; set; }
}
Within my "main" programm I got this:
MyDLL.Receiver receiver = new MyDLL.Receiver();
receiver.initializeMeasure += new MyDLL.Receiver.JsonEventHandler(createFolder); // here comes the error
And of cause this is the method:
public void createFolder(object sender, MyDLL.Receiver.JsonEventArgs e)
{
//code
}
Any suggestion where this error comes from?

How to publish and subscribe to events across different classes

Goal: To change a image on a form when either udp or tcp uses its send method
Problem: I have no idea how to get the event, eventhandler and delegates set up correctly
Send Interface
interface ISendData
{
void Send();
}
Tcp Connection class
//Need some type of delegate??
public class TCPconnection : ISendData
{
void Send()
{
//how invoke/fire a send Event?
}
}
UDP Connection class
//Need some type of delegate??
public class UDPConnection : ISendData
{
void Send()
{
//how invoke/fire a send event?
}
}
the winform which 'should' subscribe to seeing the fired events
public class myForm
{
private DataWatcher datawatcher = new DataWatcher();
private Image statusIndicator = null;
public myform()
{
initComponents();
datawatcher.DataSendActive += new DataWatcherSendHandler(DataSending);
datawatcher.DataSendInactive += new DataWatcherSendHandler(NoDataSending);
}
public void DataSending(object sender, DataWatcherArgs e)
{
statusIndicator = Properties.resources.greenLight;
}
public void NoDataSending(object sender, DataWatcherArgs e)
{
statusIndicator = Properties.resources.redLight;
}
}
The Event/Event handler?? But I really have no Idea what I'm doing here to make this work
public delegate void EventHandler(object sender, EventArgs e);
class DataWatcher
{
public event EventHandler DataSendActive;
public event EventHandler DataSendInactive;
protected virtual void onDataSendActive(System.EventArgs e)
{
if (DataSendActive != null)
{
DataSendActive(this, e);
}
}
protected virtual void onDataSendInactive(System.EventArgs e)
{
if (DataSendInactive != null)
{
DataSendInactive(this, e);
}
}
}
There are many conventions used to do this. Here's my little implementation.
public enum ActivityState
{
Sending,
Receiving,
Idle
}
public interface IDataTransferManager
{
// This event will fire when the activity state changes.
// note that Action<T> is introduced in .NET 3.5
// if you're using .NET 2.0, you can use a delegate.
event Action<ActivityState> DataActivityStateChange;
void Send(byte[] data);
//byte[] Receive();
// ... more methods ... //
}
Now the TcpConnection class will implement this.
public class TcpConnection : IDataTransferManager
{
public event Action<ActivityState> DataActivityStateChange;
public void Send(byte[] data)
{
// we're sending data. fire the change event
FireDataActivityStateChange(ActivityState.Sending);
//TODO: send the data
// we're done sending. Fire the change event
FireDataActivityStateChange(ActivityState.Idle);
}
private void FireDataActivityStateChange(ActivityState state)
{
// helper method, so I don't have to check the event
// to avoid null reference exceptions.
if (DataActivityStateChange != null)
DataActivityStateChange(state);
}
}
Here's the setup for your Form.
class MyForm // :Form
{
IDataTransferManager dataManager;
public MyForm()
{ // here, usually an instance will be passed in,
// so there's only one instance throughout the application.
// let's new up an instance for explanation purposes.
dataManager = new TcpConnection();
dataManager.DataActivityStateChange += (state) =>
{
// NOTE: if you don't like inline,
// you can point this labda to a method.
switch (state)
{
case ActivityState.Sending:
// change the image to the spinning toilet ball
break;
case ActivityState.Receiving:
// change the image to the spinning toilet ball, but reverse :P
break;
case ActivityState.Idle:
// hide it ?
break;
}
};
}
}
Here is a simple example of how you could implement an event for sending and not sending and subscribe to it
public class Connection
{
//Set up an event
public event EventHandler DataSending;
public event EventHandler DataNotSending
//This method will trigger the event for sending
private void OnDataSending()
{
if (DataSending!= null) { DataSending(this, EventArgs.Empty); }
}
//this method will trigger the event for finished sending
private void OnDataNotSending()
{
if (DataNotSending!= null) { DataNotSending(this, EventArgs.Empty); }
}
//This method performs your send logic
public void Send()
{
//Call your method that tells the event to be raised
OnDataSending();
//Then put your send code
OnDataNotSending(); //we're done!
}
}
This is how you use it in a consuming program
public class myForm
{
//This method is the one that sets up the
//instance and subscribes to the event
public myForm()
{
Connection con = new Connection();
con.DataSending += new EventHandler(con_DataSending);
con.DataNotSending += new EventHander(con_DataNotSending);
}
void con_DataSending(object sender, EventArgs e)
{
//Put your subscription logic here.
//Whatever you want to do in response to a send
}
void con_DataNotSending(object sender, EventArgs e)
{
//Put your subscription logic here.
//Respond to it not sending
}
}

One shot events using Lambda in C#

I find myself doing this sort of thing quite often:-
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;
Basically I only want to attach an event handler to listen for one shot from the event, I no longer want to stay attached after that. Quite often that "snippert of code" is just one line.
My mind is going a bit numb, I'm sure there must be something I can do so I don't need to repeat all this overhead. Bear in mind that EventHandler may well be EventHandler<T>.
Any ideas how I can tidy up the repeative part of the code and just leave the snippet in a Lambda?
You could attache a permanent event handler to the event. The event handler then invokes "one shot event handlers" that are added to an internal queue:
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();
Code:
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);
}
}
Test class:
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);
}
}
You can use reflection:
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);
}
}
Use it like so:
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!"));
If you can use the Reactive Extensions for .NET, you can simplify this.
You can make an Observable from an event, and only listen for the first element using .Take(1), to do your small snippet of code. This turns this entire process into a couple of lines of code.
Edit: In order to demonstrate, I've made a full sample program (I'll paste below).
I moved the observable creation and subscription into a method (HandleOneShot). This lets you do what you're attempting with a single method call. For demonstrating, I made a class with two properties that implements INotifyPropertyChanged, and am listening for the first property changed event, writing to the console when it occurs.
This takes your code, and changes it to:
HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent", e => {
// Small snippet of code here
});
Notice that all of the subscription/unsubscription happens automatically for you behind the scenes. There's no need to handle putting in the subscription manually - just Subscribe to the Observable, and Rx takes care of this for you.
When run, this code prints:
Setup...
Setting first property...
**** Prop2 Changed! /new val
Setting second property...
Setting first property again.
Press ENTER to continue...
You only get a single, one shot trigger of your event.
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();
}
}
}
Another user encountered a very similar problem, and I believe the solution in that thread applies here.
In particular, what you have is not an instance of the publish/subscribe pattern, its a message queue. Its easy enough to create your own message queue using a Queue{EventHandler}, where you dequeue events as you invoke them.
So instead of hooking on to an event handler, your "one-shot" events should expose a method allowing clients to add an function to the message queue.
Does it work? If so, then I say go for it. For a one-shot event that looks to be quite elegant.
What I like...
If s is garbage collected, so will the event handler.
The detaching code is right next to the attaching code, making it easy to see what you are are doing.
You might be able to generalize it, but I'm not entierly sure how to because I can't seem to get a pointer to a event.
Personally, I just create a specialized extension method for whatever type has the event I'm dealing with.
Here's a basic version of something I am using right now:
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;
}
}
}
The reason I think this is the best solution is because you often don't need to just handle the event one time. You also often need to check if the event has already passed... For instance, here is another version of the above extension method that uses an attached property to check if the element is already loaded, in which case it just calls the given handler right away:
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;
}
}
}
You probably want to work with the new async/await idioms.
Usually when I need to execute an event handler one-shot like you described, what I really need is something like:
await variableOfSomeSort.SomeMethodAsync();
//small snippet of code here
Why not do use the delegate stack built into the event?
Something like...
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);
}
}

Categories