Is it possible to send reference of 'sender' without specifying it explicitly as a parameter in delegate-based event handling?
I have a internal class which raises some events and I want to call the events explicitly for test purposes.
public class Manager {
public class DataStruct {
public int Id { get; private set; }
public event EventHandler Event1; // Can't be called by other classes
public void fireEvent1(Event1();} // So another caller...
// Delegates *can* be called by other classes
public delegate void DelegateHandler(DataStruct sender);
public DelegateHandler NewEvent;
public void DelegateHandler(DataStruct sender) {
MessageBox.Show(string.Format(
"{0} raises event", sender.Id));
}
}
}
// Form1 ///////////////////////////////////////////////////////////////////
partial class Form1 {
Manager.DataStruct dsRaiser, dsListener;
private void Form1_Load(object sender, EventArgs e) {
dsRaiser.Event1 += dsListener.SOME_HANDLER;
dsRaiser.NewEvent += dsListener.DelegateHandler;
}
private void button1_Click(object sender, ...) {
dsRaiser.fireEvent1(); // No argument needed but fireEvent1, not Event1().
}
private void button2_Click(object sender, ...) {
dsRaiser.NewEvent(dsRaiser); // Way to omit parameter dsRaiser?
}
//////////////////////////////////////////////////////////////////////////
If your handler method needs to use the sender's reference, then you HAVE to pass that reference.
If not, just declare a void parameterless delegate, like Action.
But when thinking of events, that parameter should be passed by the class that raises the event itself. (Remember events are not meant to be called from outside).
So, if you really want to use a simple delegate instead of an event, you will have to pass the parameters.
If you need the sender, you will need to do exactly what you did with the event: create a method to "raise" the delegate, and in that method you pass this as the sender.
But considering you have to do exactly the same thing in both cases, I'd surely use the event.
public class DataStruct {
public int Id { get; private set; }
public event EventHandler Event1; // Can't be called by other classes
// you need to pass those parameters to the event when called.
public void fireEvent1{Event1(this, new EventArgs());}
// Delegates *can* be called by other classes, but only with all parameters passed.
public delegate void DelegateHandler(DataStruct sender);
public DelegateHandler NewEvent;
// To avoid passing parameters, you need to do exactly what you did with the event
public void RaiseDelegate() { NewEvent(this); }
public void DelegateHandler(DataStruct sender) {
MessageBox.Show(string.Format(
"{0} raises event", sender.Id));
}
}
Yes, it is possible.
Just store the sender inside the subscription when subscribing to an event.
If we had a delegate declared like this:
public Action NewEvent; // No need to be DelegateHandler
Then we can use c# compiler to generate such a subscription for us using anonymous delegates:
dsRaiser.NewEvent += () =>
{
dsListener.DelegateHandler(dsRaiser);
};
Anything, which we reference inside our anonymous handler is automatically captured for us (both dbListener and dsRaiser in this case).
Or, if we want explicit declaration of the subscription:
class MySubscription
{
private readonly DataStruct _raiser;
private readonly DataStruct _listener;
public MySubscription(DataStruct raiser, DataStruct listener)
{
_raiser = raiser;
_listener = listener;
}
public void HandleTheSubscription()
{
_listener.DelegateHandler(_raiser);
}
}
And this is how we subscribe:
private void Form1_Load(object sender, EventArgs e) {
var mySubscription = new MySubscription(dsRaiser,dsListener);
dsRaiser.NewEvent += mySubscription.HandleTheSubscription;
}
As you can see MySubscription is defined by us and we can store any objects there.
Related
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());
}
}
I have a class that calls another class - the new class has events that I have defined for it. I am subscribed to the events in my calling class but my calling class does not seem to be able to get the EventArgs. I know I must be doing something ignorant here but I don't know what.
My code abbreviated below. WorkControl is the main process and calls MyProcess which executes some code and fires off the event.
public class WorkControl
{
public MyProcess myp;
public WorkControl()
{
myp.InBoxShareDisconnected += OnShareFolderDisconnected();
}
private EventHandler OnShareFolderDisconnected<NetworkShareDisconnectedEventArgs>()
{
// How do I get my EventArgs from the event?
throw new NotImplementedException();
}
}
public class MyProcess
{
public void MyDisconnectTrigger
{
NetworkShareDisconnectedEventArgs e =
new NetworkShareDisconnectedEventArgs(path, timestamp, connected);
OnInBoxShareDisconnected(e);
}
public event EventHandler<NetworkShareDisconnectedEventArgs> InBoxShareDisconnected;
protected void OnInBoxShareDisconnected(NetworkShareDisconnectedEventArgs e)
{
// InBoxShareDisconnected(this, e);
InBoxShareDisconnected.SafeInvoke(this, e);
}
}
You have a couple problems. Your MyProcess class shouldn't raise events in the constructor and the MyWorker class needs to have an instance of MyProcess to attach the event to. The other problem is that you need to declare the event handler correctly.
Lets look at the proper event pattern for your producer MyProcess class:
public class MyProcess
{
public event EventHandler<NetworkShareDisconnectedEventArgs> InBoxShareDisconnected;
public MyProcess()
{
//This doesn't really do anything, don't raise events here, nothing will be
//subscribed yet, so nothing will get it.
}
//Guessing at the argument types here
public void Disconnect(object path, DateTime timestamp, bool connected)
{
RaiseEvent(new NetworkShareDisconnectedEventArgs(path, timestamp, connected));
}
protected void RaiseEvent(NetworkShareDisconnectedEventArgs e)
{
InBoxShareDisconnected?.Invoke(this, e);
}
}
And now we can look at your consumer class:
public class WorkControl
{
private MyProcess _myProcess;
public WorkControl(MyProcess myProcess)
{
_myProcess = myProcess; //Need to actually set it to an object
_myProcess.InBoxShareDisconnected += HandleDisconnected;
}
private void HandleDisconnected(object sender, NetworkShareDisconnectedEventArgs e)
{
//Here you can access all the properties of "e"
}
}
So now you can consume the events in the consumer class and have access to all the properties of the NetworkShareDisconnectedEventArgs arguments. This is a pretty standard event producer/consumer model.
I'm used to using delegate EventHandler for event callbacks but now I'm attempting to use event Action for invoking events. I couldn't find much info on how this can be used properly anywhere so I'm hoping someone can point me in the right direction.
I have an Action event handler that handles string objects. In my subscriber class I have public event Action<string> UploadProgress;. The event handler is invoked like this:
UploadProgress.Invoke(string.Format("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / totalToUpload));
The listener class is subscribed to this event as below:
uploadFile.UploadProgress += uploadFile_UploadProgress;
void uploadFile_UploadProgress(string obj)
{
var prog = obj;
}
When the event is invoked, I get System.NullReferenceException: Object reference not set to an instance of an object. I'm not sure what I need to change in the subscriber class to avoid this error. Can someone tell me the proper way to use event Action or provide me the link to where I can read up on it? I know how to use the normal Action but confused about declaring it as an event. Any help is appreciated.
This way is much better, send bytesToUpload and totalToUpload through event, instead of the whole Action (right sample):
internal class Program
{
private static void Main(string[] args)
{
SomeClass someClass = new SomeClass();
someClass.UploadProgress += SomeClass_UploadProgress;
someClass.DoSomeUpload();
}
private static void SomeClass_UploadProgress(object sender, UploadEventArgs e)
{
string s = string.Format("sending file data {0:0.000}%", (e.BytesSoFar * 100.0f) / e.TotalToUpload);
Console.WriteLine(s);
}
}
public class UploadEventArgs : EventArgs
{
public float BytesSoFar { get; set; }
public float TotalToUpload { get; set; }
}
public class SomeClass
{
public event EventHandler<UploadEventArgs> UploadProgress;
public void DoSomeUpload()
{
if (UploadProgress != null)
{
UploadEventArgs e = new UploadEventArgs();
e.BytesSoFar = 123f;
e.TotalToUpload = 100000f;
UploadProgress.Invoke(this, e);
}
}
}
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?
I would like to pass a reference of a method into another method and store it as a variable. Later, I would like to use this reference to define an event handler.
When making an event handler, a method reference is passed like:
myButton.Click += new RoutedEventHandler(myButton_Click);
And if you look at the constructor for "RoutedEventHandler" from intelliSense, it looks like:
RoutedEventHandler(void(object, RoutedEventArgs))
What I would like to do is pass the method "myButton_Click" to a different static method and then create an event handler there. How do I pass the reference to the static method? I tried the following but it doesn't compile:
public class EventBuilder
{
private static void(object, RoutedEventArgs) _buttonClickHandler;
public static void EventBuilder(void(object, RoutedEventArgs) buttonClickHandler)
{
_buttonClickHandler = buttonClickHandler;
}
public static void EnableClickEvent()
{
myButton.Click += new RoutedEventHandler(_buttonClickHandler);
}
}
Thanks,
Ben
To reference a Method Reference (called a delegate in .NET), use the Handler name, rather than the signature.
public class EventBuilder
{
private static RoutedEventHandler _buttonClickHandler;
public EventBuilder(RoutedEventHandler buttonClickHandler)
{
_buttonClickHandler = buttonClickHandler;
}
public static void EnableClickEvent()
{
myButton.Click += new RoutedEventHandler(_buttonClickHandler);
}
}
try
private static delegate void(object sender, RoutedEventArgs e) _buttonClickHandler;