-= new EventHandler(Method) vs -= Method when passing method as parameter - c#

What is the difference between -= new EventHandler(Method) to -= Method
when the method passing as parameter?
Why does the removeNew failed to unsubscribe?
see the following class:
public class Class1
{
public EventHandler _eh;
public void OnEvent()
{
if (_eh != null)
{
_eh.Invoke("", new EventArgs());
}
}
public void remove(EventHandler evHandler)
{
// unsubscribe successfully
_eh -= evHandler;
}
public void removeNew(EventHandler evHandler)
{
// failed to unsubscribe
_eh -= new EventHandler(evHandler);
}
}
Update:
#SchabseLaks, just to clearify my questation I'm adding the code that call this methods:
public partial class Form1 : Form
{
Class1 c1 = new Class1();
public Form1()
{
InitializeComponent();
c1._eh += Hello;
}
private void button1_Click(object sender, EventArgs e)
{
c1.OnEvent();
}
private void Hello(object sender, EventArgs e)
{
MessageBox.Show("hello");
}
private void button2_Click(object sender, EventArgs e)
{
c1.removeNew(Hello);
}
private void button3_Click(object sender, EventArgs e)
{
c1.remove(Hello);
}
}

A delegate can only be created from a method.
new EventHandler(evHandler) is shorthand for new EventHandler(evHandler.Invoke), because Invoke is the method on any delegate type that actually calls the delegate.
Since your _eh doesn't have evHandler.Invoke as a handler, that does nothing.

The syntax of EventHandler is often confusing. The key to understanding it is to realise it overrides the Equals operator such that one EventHandler is equal to another because they hold the same Delegate (or list of delegates) as the events target(s).
So;
var x = new EventHandler(myDelegate);
var y = new EventHandler(myDelegate);
Assert.IsTrue(x == y);
var x = new EventHandler(myDelegate);
var y = new EventHandler(anotherDelegate);
Assert.IsFalse(x == y);
When you += or -= a new instance of EventHandler such as
myHandler -= new EventHandler(someDelegate);
the new EventHander is passed into the decrement method of the myHandler instance where its target delegate is compared to those already in the list. Its the Target that is being removed, not the new EventHandler instance. Conversely when you;
myHandler += new EventHandler(someDelegate);
The someDelegate is added to the existing list of delegates on the target multicast delegate called myHandler. The new EventHandler is discarded and is just a cargo carrier for that single increment method call.

Related

Wiring and unwiring events in C# with arguments

i am trying to make use of events in C#. I am really new to events. Following is my code.
public void GetVoltage(Object objName, Object objCcaVar)
{
DynamicSystemVariable mysys = new DynamicSystemVariable("VTS::M9_Ch3", "AvgVoltage");
mysys.ValueChanged += (sender, e) => mysys_ValueChanged(sender, e, ""); ;
}
void mysys_ValueChanged(object sender, EventArgs e,String name)
{
DynamicSystemVariable mysys = (DynamicSystemVariable)sender;
Output.WriteLine(mysys.GetValue().ToString());
Output.WriteLine("System Variable Changed");
if (_unwireEvent)
mysys.ValueChanged -= mysys_ValueChanged;
}
It gives me following error on the line where i am unwiring it.
No overload for 'mysys_ValueChanged' matches delegate 'System.EventHandler'
I will appreciate if somebody can help me out.
Thanks
Tom
Instead of sending name as a seperate parameter, create a new class deriving from EventArgs and add name parameter to this class as a property. Something like;
public class MyEventArgs : EventArgs
{
public string Name {get; private set;}
public MyEventArgs(EventArg e, string name)
{
this.Name = name;
}
}
mysys.ValueChanged += mysys_ValueChanged(sender, new MyEventArgs("some name"));
Also do not forget to change the signiture of mysys_ValueChanged.
Note that this is just a workaround, and proper way is defining a new delegate which uses MyEventArgs and using a seperate handling method instead of an inline method.
The problem is that you haven't register mysys_ValueChanged. You registered an anonymous method.
You have to change your code to:
public void GetVoltage(Object objName, Object objCcaVar)
{
DynamicSystemVariable mysys = new DynamicSystemVariable("VTS::M9_Ch3", "AvgVoltage");
mysys.ValueChanged += this.OnEvent;
}
private void OnEvent(object sender, EventArgs e)
{
mysys_ValueChanged(sender, e, "");
}
void mysys_ValueChanged(object sender, EventArgs e,String name)
{
DynamicSystemVariable mysys = (DynamicSystemVariable)sender;
Output.WriteLine(mysys.GetValue().ToString());
Output.WriteLine("System Variable Changed");
if (_unwireEvent)
mysys.ValueChanged -= OnEvent; // Unregister what you've registered.
}
EDIT
Having new input it could be solved that way:
public void GetVoltage(Object objName, Object objCcaVar)
{
DynamicSystemVariable mysys = new DynamicSystemVariable("VTS::M9_Ch3", "AvgVoltage");
EventHandler<EventArgs> handler = null;
handler = (s, e) => mysys_ValueChanged(s, e, "", handler);
mysys.ValueChanged += handler;
}
void mysys_ValueChanged(object sender, EventArgs e, String name, EventHandler<EventArgs> handler)
{
DynamicSystemVariable mysys = (DynamicSystemVariable)sender;
Output.WriteLine(mysys.GetValue().ToString());
Output.WriteLine("System Variable Changed");
if (_unwireEvent)
mysys.ValueChanged -= handler; // Unregister what you've registered.
}
I haven't tried it but I think this could work - but in my opinion it's hacked.

Delegate with generic list signature for passing data to another form

I'm quite new in C#, so I'm struggling with this more than two days. I hope that some one can help me out with this one.
Below some simplified code from my application.
I want to pass a List from Form1 to Form2 using delegate and event.
How can I do this? I read tons of explanations about events and delegates, but I still can't figure it out, how this really works.
Form1:
public delegate List<string> ProfileImportEventHandler();
public event ProfileImportEventHandler ProfileImported;
private void btnImport_Click(object sender, EventArgs e)
{
// raise an event
OnProfileImported();
}
protected virtual void OnProfileImported()
{
if (ProfileImported != null) // check if there are subscribers
{
ProfileImported();
}
}
Form2:
public partial class Form2 : Form
{
Form1 frm1;
public Form1()
{
// Constructor logic
frm1.ProfileChanged += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
}
}
List<string> Form1_OnProfileImported()
{
// TO DO
}
UPDATE
None of the solutions worked so far. Here is what I have already tried:
Form 2
// use generic list for profiles that will be imported from USB-Stick
private List<string> profilePaths = new List<string>();
public delegate void ProfileImportEventHandler(object sender, ProfileImportEventArgs e);
public event ProfileImportEventHandler ProfileImported;
public delegate void ImportButtonClickedEventHandler();
public event ImportButtonClickedEventHandler ButtonImportClicked;
public delegate void HaveDataDelegate(IList<string> data);
public event HaveDataDelegate HaveData;
//....
private void btnImport_Click(object sender, EventArgs e)
{
// do something...
// raise an event
var ea = new ProfileImportEventArgs(profilePaths);
OnProfileImported(ea);
OnButtonImportClicked();
// When there is data:
var copy = HaveData; // Use copy to avoid race conditions
if (copy != null)
{
copy(profilePaths);
}
// close form
this.Dispose();
}
protected virtual void OnProfileImported(ProfileImportEventArgs ea)
{
if (ProfileImported != null) // check if there are any subscribers
{
ProfileImported(this, ea);
}
}
protected virtual void OnButtonImportClicked()
{
if (ButtonImportClicked != null)
{
// fire event
ButtonImportClicked();
}
}
Form 1
public partial class frm_1 : Form
{
// child form
frm_2 frm2;
public frm_1()
{
InitializeComponent();
// do something...
// not sure if this is correct code and the correct place for it
frm2 = new frm_2();
frm2.ProfileImported += new frm_2.ProfileImportEventHandler(frm2_OnProfileImported);
//frm2.ProfileImported += frm2_OnProfileImported;
frm2.ButtonImportClicked += new frm_2.ImportButtonClickedEventHandler(frm2_ButtonImportClicked);
// In creation/init:
frm2.HaveData += DataFromForm2;
}
void frm2_OnProfileImported(object sender, ProfileImportEventArgs e)
{
// do something
}
void frm2_ButtonImportClicked()
{
// do something
}
private void DataFromForm2(IList<string> data)
{
// Process the data from Form2.
}
}
What am I still missing? Thank you for your help.
frm1.ProfileChanged += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
[…]
List<string> frmLoadProfileUSB_OnProfileImported()
First those names do not match. Second, with matching signatures you do not need (since C#2 if I recall correctly) to explicitly create the delegate. Thus:
frm1.ProfileChanged += frmLoadProfileUSB_OnProfileImported;
However, I think you have the event in the wrong place. It appears it is Form2 trying to pass data to Form1. Thus the event needs to be on Form2, with a delegate that is passed the data. Thus:
In Form2
public delegate void HaveDataDelegate(IList<string> data);
public event HaveDataDelegate HaveData;
// When there is data:
var copy = HaveData; // Use copy to avoid race conditions
if (copy != null) {
copy(data);
}
In Form1
// In creation/init:
Form2Instance.HaveData += DataFromForm2;
private void DataFromForm2(IList<string> data) {
// Process the data from Form2.
}
It's better not to use strong coupling.
So best solution here would be to store data in database or create proxy-object (class/struct).
like:
public (static) class ProfileChangesMonitor
{
...your logic here
}
If you want to use event handlers, you should follow the general pattern, defining a class that inherits EventArgs (supposing you want to involve a list in the event) in this way:
// Event Args
public class ProfileImportEventArgs : EventArgs {
private IList<string> list;
public ProfileImportEventArgs(IList<string> list) {
this.list = list;
}
public IList<string> List {
get {
return this.list;
}
}
}
// Event Handler Delegate
public delegate void ProfileImportEventHandler(object sender, ProfileImportEventArgs e);
// Form1:
public event ProfileImportEventHandler ProfileImported;
// ...
private void btnImport_Click(object sender, EventArgs e)
{
// raise an event
List<string> list = new List();
// Add something to list if needed
var ea = new ProfileImportEventArgs(list);
OnProfileImported(ea);
// Use ea.list here if necessary
}
protected virtual void OnProfileImported(ProfileImportEventArgs ea)
{
if (ProfileImported != null) { // check if there are subscribers
ProfileImported(this, ea);
}
}
// Form2:
public partial class Form2 : Form
{
Form1 frm1;
public Form1()
{
// Constructor logic
// TODO: Instantiate frm1 first.
frm1.ProfileImported += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
}
}
private void frmLoadProfileUSB_OnProfileImported(object sender, ProfileImportEventArgs e)
{
// Use and/or modify e.List if needed
}

how to remove the listening of callback on anonymous method?

I wrote some class:
public class A
{
public A()
{
serviceAdapter.CompletedCallBackEvent += new EventHandler( foo );
.
.
.
}
void foo(object sender, EventArgs e)
{
serviceAdapter.CompletedCallBackEvent -= new EventHandler( foo );
}
}
Now, i want to change this callback listener with some anonymous - but i don't know how to remove the callback listener in the anonymous method .
class A
{
public A()
{
serviceAdapter.CompletedCallBackEvent += delegate( object sender, EventArgs ee )
{
... need to remove the listener to the event.
}
}
}
You could simply assign your delegate/handler to a private variable.
private EventHander _handler = null;
public A()
{
_handler = delegate( object sender, EventArgs ee)
{
ServiceAdapter.CompletedCallBackEvent -= _handler;
};
ServiceAdapter.CompletedCallBackEvent += _handler;
}
You can't remove the anonymous delegate like that. See MSDN article on anonymous delegates. Also worth reading this article
You may be able to do:
public A()
{
EventHandler myHandler = null;
myHandler = new EventHandler(delegate(object s, EventArgs e)
{
serviceAdapter.CompletedCallbackEvent -= myHandler;
});
serviceAdapter.CompletedCallBackEvent += myHandler;
}

WinForm Applications event handler

I am just trying my hand at some WinForm Applications and was creating a simple event handler, but I get an error message. Code:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public delegate void MyHandler1(object sender, EventArgs e);
public Form1()
{
InitializeComponent();
List<string> names = new List<string>();
names.Add("S");
names.Add("I");
names.Add("G");
MyHandler1 onClicked = new MyHandler1(clicked);
listBox1.DataSource = names;
listBox1.Click += onClicked;
}
public void clicked(object sender, EventArgs e)
{
label1.ResetText();
label1.Text = listBox1.SelectedItem.ToString();
}
}
}
Error:
Error 1 Cannot implicitly convert type 'WindowsFormsApplication1.Form1.MyHandler1' to 'System.EventHandler'
The reason that your code doesn't compile is that implicit conversions do not exist between different delegate-types, even when the signatures are 'compatible'.
Try either of these:
// Implicit method-group conversion, should work from C# 2.0 or later.
// Essentially shorthand for listBox1.Click += new EventHandler(clicked);
listBox1.Click += clicked;
// Creating a delegate-instance from a 'compatible' delegate,
// a trick I recently learnt from his highness Jon Skeet
listBox1.Click += new EventHandler(onClicked);
As an aside, unless the intention is to learn how to use delegates, I suggest you don't create your own delegate-type when one that comes with the framework will do the job.
Just use this code instead:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public delegate void MyHandler1(object sender, EventArgs e);
public Form1()
{
InitializeComponent();
List<string> names = new List<string>();
names.Add("S");
names.Add("I");
names.Add("G");
listBox1.DataSource = names;
listBox1.Click += clicked;
}
public void clicked(object sender, EventArgs e)
{
label1.ResetText();
label1.Text = listBox1.SelectedItem.ToString();
}
}
}
You don't really need the EventHandler1 in order to listen to handle an event with clicked method.
You do not need to create an entirely new delegate type to subscribe to an existing event. The event you are subscribing to already uses the existing System.EventHandler delegate type.
You only need to do:
listBox1.Click += new EventHandler(onClicked);

How to subscribe to other class' events in C#?

A simple scenario: a custom class that raises an event. I wish to consume this event inside a form and react to it.
How do I do that?
Note that the form and custom class are separate classes.
public class EventThrower
{
public delegate void EventHandler(object sender, EventArgs args) ;
public event EventHandler ThrowEvent = delegate{};
public void SomethingHappened() => ThrowEvent(this, new EventArgs());
}
public class EventSubscriber
{
private EventThrower _Thrower;
public EventSubscriber()
{
_Thrower = new EventThrower();
// using lambda expression..could use method like other answers on here
_Thrower.ThrowEvent += (sender, args) => { DoSomething(); };
}
private void DoSomething()
{
// Handle event.....
}
}
Inside your form:
private void SubscribeToEvent(OtherClass theInstance) => theInstance.SomeEvent += this.MyEventHandler;
private void MyEventHandler(object sender, EventArgs args)
{
// Do something on the event
}
You just subscribe to the event on the other class the same way you would to an event in your form. The three important things to remember:
You need to make sure your method (event handler) has the appropriate declaration to match up with the delegate type of the event on the other class.
The event on the other class needs to be visible to you (ie: public or internal).
Subscribe on a valid instance of the class, not the class itself.
Assuming your event is handled by EventHandler, this code works:
protected void Page_Load(object sender, EventArgs e)
{
var myObj = new MyClass();
myObj.MyEvent += new EventHandler(this.HandleCustomEvent);
}
private void HandleCustomEvent(object sender, EventArgs e)
{
// handle the event
}
If your "custom event" requires some other signature to handle, you'll need to use that one instead.

Categories

Resources