Related
Is it possible to unsubscribe an anonymous method from an event?
If I subscribe to an event like this:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
I can un-subscribe like this:
MyEvent -= MyMethod;
But if I subscribe using an anonymous method:
MyEvent += delegate(){Console.WriteLine("I did it!");};
is it possible to unsubscribe this anonymous method? If so, how?
Action myDelegate = delegate(){Console.WriteLine("I did it!");};
MyEvent += myDelegate;
// .... later
MyEvent -= myDelegate;
Just keep a reference to the delegate around.
One technique is to declare a variable to hold the anonymous method which would then be available inside the anonymous method itself. This worked for me because the desired behavior was to unsubscribe after the event was handled.
Example:
MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
Since C# 7.0 local functions feature has been released, the approach suggested by J c becomes really neat.
void foo(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
So, honestly, you do not have an anonymous function as a variable here. But I suppose the motivation to use it in your case can be applied to local functions.
From memory, the specification explicitly doesn't guarantee the behaviour either way when it comes to equivalence of delegates created with anonymous methods.
If you need to unsubscribe, you should either use a "normal" method or retain the delegate somewhere else so you can unsubscribe with exactly the same delegate you used to subscribe.
In 3.0 can be shortened to:
MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
Instead of keeping a reference to any delegate you can instrument your class in order to give the event's invocation list back to the caller. Basically you can write something like this (assuming that MyEvent is declared inside MyClass):
public class MyClass
{
public event EventHandler MyEvent;
public IEnumerable<EventHandler> GetMyEventHandlers()
{
return from d in MyEvent.GetInvocationList()
select (EventHandler)d;
}
}
So you can access the whole invocation list from outside MyClass and unsubscribe any handler you want. For instance:
myClass.MyEvent -= myClass.GetMyEventHandlers().Last();
I've written a full post about this tecnique here.
Kind of lame approach:
public class SomeClass
{
private readonly IList<Action> _eventList = new List<Action>();
...
public event Action OnDoSomething
{
add {
_eventList.Add(value);
}
remove {
_eventList.Remove(value);
}
}
}
Override the event add/remove methods.
Keep a list of those event handlers.
When needed, clear them all and re-add the others.
This may not work or be the most efficient method, but should get the job done.
If you want to be able to control unsubscription then you need to go the route indicated in your accepted answer. However, if you are just concerned about clearing up references when your subscribing class goes out of scope, then there is another (slightly convoluted) solution which involves using weak references. I've just posted a question and answer on this topic.
One simple solution:
just pass the eventhandle variable as parameter to itself.
Event if you have the case that you cannot access the original created variable because of multithreading, you can use this:
MyEventHandler foo = null;
foo = (s, ev, mehi) => MyMethod(s, ev, foo);
MyEvent += foo;
void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance)
{
MyEvent -= myEventHandlerInstance;
Console.WriteLine("I did it!");
}
If the best way is to keep a reference on the subscribed eventHandler, this can be achieved using a Dictionary.
In this example, I have to use a anonymous method to include the mergeColumn parameter for a set of DataGridViews.
Using the MergeColumn method with the enable parameter set to true enables the event while using it with false disables it.
static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();
public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {
if(enable) {
subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
dg.Paint += subscriptions[dg];
}
else {
if(subscriptions.ContainsKey(dg)) {
dg.Paint -= subscriptions[dg];
subscriptions.Remove(dg);
}
}
}
if you want refer to some object with this delegate, may be you can use Delegate.CreateDelegate(Type, Object target, MethodInfo methodInfo)
.net consider the delegate equals by target and methodInfo
There is a way to solve this by implementing the closure yourself instead of a lambda expression.
Assume that the class to be used as a capture variable is as follows.
public class A
{
public void DoSomething()
{
...
}
}
public class B
{
public void DoSomething()
{
...
}
}
public class C
{
public void DoSomething()
{
...
}
}
These classes will be used as capture variables, so we instantiate them.
A a = new A();
B b = new B();
C c = new C();
Implement the closure class as shown below.
private class EventHandlerClosure
{
public A a;
public B b;
public C c;
public event EventHandler Finished;
public void MyMethod(object, MyEventArgs args)
{
a.DoSomething();
b.DoSomething();
c.DoSomething();
Console.WriteLine("I did it!");
Finished?.Invoke(this, EventArgs.Empty);
}
}
Instantiate the closure class, create a handler, then subscribe to the event and subscribe to the lambda expression that unsubscribes from the closure class's Finished event.
var closure = new EventHandlerClosure
{
a = a,
b = b,
c = c
};
var handler = new MyEventHandler(closure.MyMethod);
MyEvent += handler;
closure.Finished += (s, e)
{
MyEvent -= handler;
}
I discovered this quite old thread recently for a C# project and found all the answers very useful. However, there was one aspect that didn't work well for my particular use case - they all put the burden of unsubscribing from an event on the subscriber. I understand that one could make the argument that it's the subscribers job to handle this, however that isn't realistic for my project.
My primary use case for events is for listening to timers to sequence animations (it's a game). In this scenario, I use a lot of anonymous delegates to chain together sequences. Storing a reference to these isn't very practical.
In order to solve this, I've created a wrapper class around an event that lets you subscribe for a single invocation.
internal class EventWrapper<TEventArgs> {
private event EventHandler<TEventArgs> Event;
private readonly HashSet<EventHandler<TEventArgs>> _subscribeOnces;
internal EventWrapper() {
_subscribeOnces = new HashSet<EventHandler<TEventArgs>>();
}
internal void Subscribe(EventHandler<TEventArgs> eventHandler) {
Event += eventHandler;
}
internal void SubscribeOnce(EventHandler<TEventArgs> eventHandler) {
_subscribeOnces.Add(eventHandler);
Event += eventHandler;
}
internal void Unsubscribe(EventHandler<TEventArgs> eventHandler) {
Event -= eventHandler;
}
internal void UnsubscribeAll() {
foreach (EventHandler<TEventArgs> eventHandler in Event?.GetInvocationList()) {
Event -= eventHandler;
}
}
internal void Invoke(Object sender, TEventArgs e) {
Event?.Invoke(sender, e);
if(_subscribeOnces.Count > 0) {
foreach (EventHandler<TEventArgs> eventHandler in _subscribeOnces) {
Event -= eventHandler;
}
_subscribeOnces.Clear();
}
}
internal void Remove() {
UnsubscribeAll();
_subscribeOnces.Clear();
}
}
The side benefit of having this in a class is that you can make it private and expose only the functionality you want. For example, only expose the SubscribeOnce (and not the Subscribe) method.
public class MyClass {
private EventWrapper<MyEventEventArgs> myEvent = new EventWrapper<MyEventEventArgs>();
public void FireMyEvent() {
myEvent.Invoke(this, new MyEventEventArgs(1000, DateTime.Now));
}
public void SubscribeOnce(EventHandler<MyEventEventArgs> eventHandler) {
myEvent.SubscribeOnce(eventHandler);
}
public class MyEventEventArgs : EventArgs {
public int MyInt;
public DateTime MyDateTime;
public MyEventEventArgs(int myInt, DateTime myDateTime) {
MyInt = myInt;
MyDateTime = myDateTime;
}
}
}
The tradeoff here is more overhead for having an instance of this for each event, however in my scenario - this is an acceptable tradeoff to ensure that garbage gets collected efficiently and the code is more maintainable on the subscriber side. Full example here.
Here is a simple solution, which removes all assigned methods from an event. Also anonymous methods.
Use this code and adjust the names.
if (MyEvent != null)
foreach (Delegate del in MyEvent.GetInvocationList())
MyEvent -= (EventHandler<MyEventHandlerType>)del;
Example usage
public class SomeClass
{
public event EventHandler<NiceEventArgs> NiceEvent;
public void RemoveHandlers()
{
if (NiceEvent != null)
foreach (Delegate del in NiceEvent.GetInvocationList())
NiceEvent -= (EventHandler<NiceEventArgs>)del;
}
}
Thanks to hemme's answer, which I used as inspiration.
I know Events are always associated with Delegates. But, I am missing some core use of Events, and trying to understand that.
I created a simple Event program, as below, and it works perfectly fine.
namespace CompleteRef3._0
{
delegate void someEventDelegate();
class EventTester
{
public event someEventDelegate someEvent;
public void doEvent()
{
if (someEvent != null) someEvent();
}
}
class Program
{
static void EventHandler1()
{
Console.WriteLine("Event handler 1 called..");
}
static void EventHandler2()
{
Console.WriteLine("Event handler 2 called..");
}
static void EventHandler3()
{
Console.WriteLine("Event handler 3 called..");
}
static void Main(string[] args)
{
EventTester evt = new EventTester();
evt.someEvent += EventHandler1;
evt.someEvent += EventHandler2;
evt.someEvent += EventHandler3;
evt.doEvent();
Console.ReadKey();
}
}
}
I replaced the event declaration with delegates. That is I replaced the line public event someEventDelegate someEvent; with someEventDelegate someEvent; on the above program, and I still get the same result. Now, I was confused why we need to use Events, if it can be achieved by Delegates only. What is the real use of Events?
The modified program without events is as below -
namespace CompleteRef3._0
{
delegate void someEventDelegate();
class EventTester
{
someEventDelegate someEvent;
public void doEvent()
{
if (someEvent != null) someEvent();
}
}
class Program
{
static void EventHandler1()
{
Console.WriteLine("Event handler 1 called..");
}
static void EventHandler2()
{
Console.WriteLine("Event handler 2 called..");
}
static void EventHandler3()
{
Console.WriteLine("Event handler 3 called..");
}
static void Main(string[] args)
{
EventTester evt = new EventTester();
evt.someEvent += EventHandler1;
evt.someEvent += EventHandler2;
evt.someEvent += EventHandler3;
evt.doEvent();
Console.ReadKey();
}
}
}
Imagine you have 3 subscribers who are interested in your someEvent. Let's further imagine they are interested in receiving events from the same EventTester instance. For brevity, let's leave out the details of how the exact same instance is passed to all the clients. When I say clients, I mean any class who is a subscriber to the event.
Here is the instance:
EventTester evt = new EventTester();
They have subscribed to the event of the above instance as shown below:
Client 1
evt.someEvent += Client1Handler1;
evt.someEvent += Client1Handler2;
Client 2
evt.someEvent += Client2Handler1;
Client 3
evt.someEvent += Client3Handler1;
evt.someEvent += Client3Handler2;
Client 4:
Imagine client 4 did one of the 3 below:
// 1: See Note 1 below
evt.someEvent = null;
// 2: See Note 2 below
evt.someEvent = new someEventDelegate(MyHandler);
// 3: See Note 3 below
evt.someEvent();
//...
private void MyHandler()
{
MessageBox.Show("Client 4");
}
Note 1
Client 1, 2, and 3 will not be getting any events anymore. Why? Because Client 4 just did this evt.someEvent = null; and in EventTester you have this line of code:
if (someEvent != null) someEvent();
Since that condition will not pass anymore, no event will be raised. There is nothing wrong with the above line of code by the way. But there is the problem with using delegates: It can be assigned to.
Note 2
It has been completely over-written to a brand new instance. Now regardless of the client, they will all see a message box that says "Client 4".
Note 3
Ooops all of a sudden one of the clients is broadcasting the event.
Imagine for a second EventTester was a Button and someEvent was Click. Imagine you had multiple clients interested in the click event of this button. All of a sudden, one client decides no-one else should get notifications (Note 1). Or one client decides that when the button is clicked, it will be handled only 1 way (Note 2). Or it has made the decision that it will decide when a button is clicked even though the button may not have been clicked (Note 3).
If you have an event and one of the clients tried the above, they will not be allowed and get a compile error, like this:
Sure, you can use delegates because behind the scenes an event is a construct that wraps a delegate.
But the rationale of using events instead of delegates is the the same as for using properties instead of fields - data encapsulation. It's bad practice to expose fields (whatever they are - primitive fields or delegates) directly.
By the way, you missed a public keyword before your delegate field to make it possible in the second snippet.
Another "by the way" with the second snippet: for delegates you should use Delegate.Combine instead of "+=".
The main purpose of events is to prevent subscribers from interfering with each other. If you do not use events, you can:
Replace other subscribers by reassigning delegate(instead of using the += operator),
Clear all subscribers (by setting delegate to null),
Broadcast to other subscribers by invoking the delegate.
Source: C# in a Nutshell
public class Program
{
public static void Main()
{
Number myNumber = new Number(100000);
myNumber.PrintMoney();
myNumber.PrintNumber();
Console.ReadKey();
}
}
public class Number
{
private PrintHelper _printHelper;
public Number(int val)
{
_value = val;
_printHelper = new PrintHelper();
//subscribe to beforePrintEvent event
_printHelper.beforePrintEvent += printHelper_beforePrintEvent;
}
//beforePrintevent handler
void printHelper_beforePrintEvent(string message)
{
Console.WriteLine("BeforePrintEvent fires from {0}", message);
}
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
public void PrintMoney()
{
_printHelper.PrintMoney(_value);
}
public void PrintNumber()
{
_printHelper.PrintNumber(_value);
}
}
public class PrintHelper
{
public delegate void BeforePrintDelegate(string message);
public event BeforePrintDelegate beforePrintEvent;
public PrintHelper()
{
}
public void PrintNumber(int num)
{
if (beforePrintEvent != null)
beforePrintEvent.Invoke("PrintNumber");
Console.WriteLine("Number: {0,-12:N0}", num);
}
public void PrintDecimal(int dec)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintDecimal");
Console.WriteLine("Decimal: {0:G}", dec);
}
public void PrintMoney(int money)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintMoney");
Console.WriteLine("Money: {0:C}", money);
}
public void PrintTemperature(int num)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintTemerature");
Console.WriteLine("Temperature: {0,4:N1} F", num);
}
public void PrintHexadecimal(int dec)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintHexadecimal");
Console.WriteLine("Hexadecimal: {0:X}", dec);
}
}
This question already has answers here:
How to raise an event on Property Change?
(2 answers)
Closed 7 years ago.
I have
class A
{
B b;
//call this Method when b.Button_click or b.someMethod is launched
private void MyMethod()
{
}
??
}
Class B
{
//here i.e. a button is pressed and in Class A
//i want to call also MyMethod() in Class A after the button is pressed
private void Button_Click(object o, EventArgs s)
{
SomeMethod();
}
public void SomeMethod()
{
}
??
}
Class A has a instance of Class B.
How can this be done?
You'll need to declare a public event on class 'B' - and have class 'A' subscribe to it:
Something like this:
class B
{
//A public event for listeners to subscribe to
public event EventHandler SomethingHappened;
private void Button_Click(object o, EventArgs s)
{
//Fire the event - notifying all subscribers
if(SomethingHappened != null)
SomethingHappened(this, null);
}
....
class A
{
//Where B is used - subscribe to it's public event
public A()
{
B objectToSubscribeTo = new B();
objectToSubscribeTo.SomethingHappened += HandleSomethingHappening;
}
public void HandleSomethingHappening(object sender, EventArgs e)
{
//Do something here
}
....
You need three things (which is marked by comments in code):
Declare event in class B
Raise event in class B when something happened (in your case - Button_Click event handler executed). Keep in mind that you need to verify if there are any subscribers exists. Otherwise you will get NullReferenceException on raising event.
Subscribe to event of class B. You need to have instance of class B, which even you want to subscribe (another option - static events, but those events will be raised by all instances of class B).
Code:
class A
{
B b;
public A(B b)
{
this.b = b;
// subscribe to event
b.SomethingHappened += MyMethod;
}
private void MyMethod() { }
}
class B
{
// declare event
public event Action SomethingHappened;
private void Button_Click(object o, EventArgs s)
{
// raise event
if (SomethingHappened != null)
SomethingHappened();
SomeMethod();
}
public void SomeMethod() { }
}
Have a look at rasing an event from Class B
Have a look at
Raising an Event
Handling and Raising Events
How to: Raise and Consume Events
I would like to create a method that takes an event as an argument and adds eventHandler to it to handle it properly. Like this:
I have two events:
public event EventHandler Click;
public event EventHandler Click2;
Now I would like to pass a particular event to my method like this (pseudocode):
public AttachToHandleEvent(EventHandler MyEvent)
{
MyEvent += Item_Click;
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);
Is it possible?
I've noticed that this code worked fine, and returned to my project and noticed that when I pass an event declared in my class, it works, but when I pass event from other class it still does not work.
What I get is this error:
The event
'System.Windows.Forms.ToolStripItem.Click'
can only appear on the left hand side
of += or -=
My original answer was suitable from within the class that defined the event, but you've since updated your question to reflect that you wish to accomplish this from outside the defining class, so I've stricken that.
Only the class that defines an event can refer to the implicit delegate variable that the event uses. From outside that class, you only have access to the add and remove methods, via += and -=. This means that you can't do what you're asking, directly. You can, however, use a functional approach.
class A{
public event EventHandler Event1;
public void TriggerEvent1(){
if(Event1 != null)
Event1(this, EventArgs.Empty);
}
}
class B{
static void HandleEvent(object o, EventArgs e){
Console.WriteLine("Woo-hoo!");
}
static void AttachToEvent(Action<EventHandler> attach){
attach(HandleEvent);
}
static void Main(){
A a = new A();
AttachToEvent(handler=>a.Event1 += handler);
a.TriggerEvent1();
}
}
I did it like this:
public AttachToHandleEvent(Object obj, string EventName)
{
EventInfo mfi = obj.GetType().GetEvent(EventName);
MethodInfo mobj = mfi.GetAddMethod();
mobj.Invoke(obj, new object[] { Item_Click});
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool "Click");
Thank you all for advice. This solution could not be done without your help.
It's not possible. You can use a delegate instead of an event if that meets your needs.
Just write tool.Click += Item_Click;
Edit: From MSDN "Events can only be invoked from within the class or struct where they (it) are declared". So what you are trying to do is not possible. Could you elaborate more on your needs? Why would you want to pass an event as a parameter?
delegate void doIt(object sender, object data);
event doIt OnDoIt;
void add(doIt theDel)
{
OnDoIt += theDel;
}
void doIt1(object a, object b)
{
}
void doIt2(object a, object b)
{
}
void add()
{
add(doIt1);
add(doIt2);
}
Your question suggests that you got some mechanisms wrong:
You can't pass events!
You most probably want to pass a function as a parameter, so the calling method will call that other method at some point. In technical terms this is a delegate. I suggest using the already defined Action class. Here's an example snippet:
void MyFunction (string otherArguments, Action onFinished){
...
if (onFinished != null)
onFinished.Invoke();
}
The nice thing about this is that when calling MyFunction you can declare the Action using the inline syntax:
MyFunction("my other argument", ()=>{
///do stuff here, which will be execuded when the action is invoked
});
I pass functions/methods (instead of events) like this:
class A
{
public void something()
{
var myAction =
new Action<object, object>((sender, evArgs) => {
MessageBox.Show("hiii, event happens " + (evArgs as as System.Timers.ElapsedEventArgs).SignalTime);
});
B.timer(myAction);
}
}
class B
{
public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
myTimer.Interval = 2000;
myTimer.Start();
}
}
Giving an update to this question with an object oriented solution.
Instead of using an Action<EventHandler> that registers the event, you could create an object handling that for you
public class AEvent
{
private readonly A aInstance;
private AEvent(A instance) {
aInstance = instance;
}
public void Add(EventHandler eventHandler)
=> a.Event1 += eventHandler;
public void Remove(EventHandler eventHandler)
=> a.Event1 -= eventHandler;
public EventHandler Invoke => aInstance.Event1;
}
Then later on use that object like this:
static void Main(){
A a = new A();
AEvent aEvent = new AEvent(A)
aEvent.Add(handler);
a.Invoke();
}
One approach I haven't seen here would be to create an object which has delegates for subscribe and unsubscribe. Here is a complete example program.
class Program
{
private event EventHandler<EventArgs> eventHandler;
public static void Main(string[] args)
{
Program program = new Program();
Thing thing = new Thing(new EventWrapper<EventArgs>(
delegate(EventHandler<EventArgs> handler) { program.eventHandler += handler; },
delegate(EventHandler<EventArgs> handler) { program.eventHandler -= handler; }
));
// events are fired
program.eventHandler?.Invoke(program, EventArgs.Empty);
thing.Unsubscribe();
}
}
class Thing
{
private readonly Action<EventHandler<EventArgs>> _unsubscribeEventHandler;
public Thing(EventWrapper<EventArgs> eventHandler)
{
this._unsubscribeEventHandler = eventHandler.Unsubscribe;
eventHandler.Subscribe?.Invoke(OnEvent);
Console.WriteLine("subscribed");
}
private void OnEvent(object? sender, EventArgs e)
{
Console.WriteLine("event fired");
}
public void Unsubscribe()
{
_unsubscribeEventHandler?.Invoke(OnEvent);
Console.WriteLine("unsubscribed");
}
}
class EventWrapper<T> where T : EventArgs
{
public Action<EventHandler<T>> Subscribe { get; private set; }
public Action<EventHandler<T>> Unsubscribe { get; private set; }
public EventWrapper(Action<EventHandler<T>> subscribe, Action<EventHandler<T>> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
}
In this example, we created a new class called EventWrapper<T> which wraps delegates for += and -= and exposes them with Subscribe and Unsubscribe methods. The delegates will need to be created by the class which created the event.
I am currently having a hardtime understanding and implementing events in C# using delagates. I am used to the Java way of doing things:
Define an interface for a listener type which would contain a number of method definitions
Define adapter class for that interface to make things easier if I'm not interested in all the events defined in a listener
Define Add, Remove and Get[] methods in the class which raises the events
Define protected fire methods to do the dirty work of looping through the list of added listeners and calling the correct method
This I understand (and like!) - I know I could do this exactly the same in c#, but it seems that a new (better?) system is in place for c#. After reading countless tutorials explaining the use of delegates and events in c# I still am no closer to really understanding what is going on :S
In short, for the following methods how would I implement the event system in c#:
void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);
^ The above methods are taken from a Java application I once made which I'm trying to port over to c#.
Many many thanks!
You'd create four events, and methods to raise them, along with a new EventArgs-based class to indicate the error:
public class ExceptionEventArgs : EventArgs
{
private readonly Exception error;
public ExceptionEventArgs(Exception error)
{
this.error = error;
}
public Error
{
get { return error; }
}
}
public class Computer
{
public event EventHandler Started = delegate{};
public event EventHandler Stopped = delegate{};
public event EventHandler Reset = delegate{};
public event EventHandler<ExceptionEventArgs> Error = delegate{};
protected void OnStarted()
{
Started(this, EventArgs.Empty);
}
protected void OnStopped()
{
Stopped(this, EventArgs.Empty);
}
protected void OnReset()
{
Reset(this, EventArgs.Empty);
}
protected void OnError(Exception e)
{
Error(this, new ExceptionEventArgs(e));
}
}
Classes would then subscribe to the event using either a method or a an anonymous function:
someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{
Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");
A few things to note about the above:
The OnXXX methods are protected so that derived classes can raise the events. This isn't always necessary - do it as you see fit.
The delegate{} piece on each event declaration is just a trick to avoid having to do a null check. It's subscribing a no-op event handler to each event
The event declarations are field-like events. What's actually being created is both a variable and an event. Inside the class you see the variable; outside the class you see the event.
See my events/delegates article for much more detail on events.
You'll have to define a single delegate for that
public delegate void ComputerEvent(object sender, ComputerEventArgs e);
ComputerEventArgs would be defined like this:
public class ComputerEventArgs : EventArgs
{
// TODO wrap in properties
public Computer computer;
public Exception error;
public ComputerEventArgs(Computer aComputer, Exception anError)
{
computer = aComputer;
error = anError;
}
public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
{
}
}
The class that fires the events would have these:
public YourClass
{
...
public event ComputerEvent ComputerStarted;
public event ComputerEvent ComputerStopped;
public event ComputerEvent ComputerReset;
public event ComputerEvent ComputerError;
...
}
This is how you assign handlers to the events:
YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);
Your handler is:
private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
// do your thing.
}
The main difference is that in C# the events are not interface-based. Instead, the event publisher declares the delegate which you can think of as a function pointer (although not exactly the same :-)). The subscriber then implements the event prototype as a regular method and adds a new instance of the delegate to the event handler chain of the publisher. Read more about delegates and events.
You can also read short comparison of C# vs. Java events here.
First of all, there is a standard method signature in .Net that is typically used for events. The languages allow any sort of method signature at all to be used for events, and there are some experts who believe the convention is flawed (I mostly agree), but it is what it is and I will follow it for this example.
Create a class that will contain the event’s parameters (derived from EventArgs).
public class ComputerEventArgs : EventArgs
{
Computer computer;
// constructor, properties, etc.
}
Create a public event on the class that is to fire the event.
class ComputerEventGenerator // I picked a terrible name BTW.
{
public event EventHandler<ComputerEventArgs> ComputerStarted;
public event EventHandler<ComputerEventArgs> ComputerStopped;
public event EventHandler<ComputerEventArgs> ComputerReset;
...
}
Call the events.
class ComputerEventGenerator
{
...
private void OnComputerStarted(Computer computer)
{
EventHandler<ComputerEventArgs> temp = ComputerStarted;
if (temp != null) temp(this, new ComputerEventArgs(computer)); // replace "this" with null if the event is static
}
}
Attach a handler for the event.
void OnLoad()
{
ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator();
computerEventGenerator.ComputerStarted += new EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted);
}
Create the handler you just attached (mostly by pressing the Tab key in VS).
private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args)
{
if (args.Computer.Name == "HAL9000")
ShutItDownNow(args.Computer);
}
Don't forget to detach the handler when you're done. (Forgetting to do this is the biggest source of memory leaks in C#!)
void OnClose()
{
ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted;
}
And that's it!
EDIT: I honestly can't figure out why my numbered points all appear as "1." I hate computers.
there are several ways to do what you want. The most direct way would be to define delegates for each event in the hosting class, e.g.
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
if (ComputerStarted != null)
{
ComputerStarted.Invoke(computer);
}
}
protected void someMethod()
{
//...
computer.Started = true; //or whatever
OnComputerStarted(computer);
//...
}
any object may 'listen' for this event simply by:
Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
this.ComputerStartedHandler);
protected void ComputerStartedHandler(Computer computer)
{
//do something
}
The 'recommended standard way' of doing this would be to define a subclass of EventArgs to hold the Computer (and old/new state and exception) value(s), reducing 4 delegates to one. In this case that would be a cleaner solution, esp. with an Enum for the computer states in case of later expansion. But the basic technique remains the same:
the delegate defines the signature/interface for the event handler/listener
the event data member is a list of 'listeners'
listeners are removed using the -= syntax instead of +=
In c# events are delegates. They behave in a similar way to a function pointer in C/C++ but are actual classes derived from System.Delegate.
In this case, create a custom EventArgs class to pass the Computer object.
public class ComputerEventArgs : EventArgs
{
private Computer _computer;
public ComputerEventArgs(Computer computer) {
_computer = computer;
}
public Computer Computer { get { return _computer; } }
}
Then expose the events from the producer:
public class ComputerEventProducer
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerEventArgs> Error;
/*
// Invokes the Started event */
private void OnStarted(Computer computer) {
if( Started != null ) {
Started(this, new ComputerEventArgs(computer));
}
}
// Add OnStopped, OnReset and OnError
}
The consumer of the events then binds a handler function to each event on the consumer.
public class ComputerEventConsumer
{
public void ComputerEventConsumer(ComputerEventProducer producer) {
producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
// Add other event handlers
}
private void ComputerStarted(object sender, ComputerEventArgs e) {
}
}
When the ComputerEventProducer calls OnStarted the Started event is invoked which in turn will call the ComputerEventConsumer.ComputerStarted method.
The delegate declares a function signature, and when it's used as an event on a class it also acts as a collection of enlisted call targets. The += and -= syntax on an event is used to adding a target to the list.
Given the following delegates used as events:
// arguments for events
public class ComputerEventArgs : EventArgs
{
public Computer Computer { get; set; }
}
public class ComputerErrorEventArgs : ComputerEventArgs
{
public Exception Error { get; set; }
}
// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);
public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);
// component that raises events
public class Thing
{
public event ComputerEventHandler Started;
public event ComputerEventHandler Stopped;
public event ComputerEventHandler Reset;
public event ComputerErrorEventHandler Error;
}
You would subscribe to those events with the following:
class Program
{
static void Main(string[] args)
{
var thing = new Thing();
thing.Started += thing_Started;
}
static void thing_Started(object sender, ComputerEventArgs e)
{
throw new NotImplementedException();
}
}
Although the arguments could be anything, the object sender and EventArgs e is a convention that's used very consistently. The += thing_started will first create an instance of the delegate pointing to target method, then add it to the event.
On the component itself you would typically add methods to fire the events:
public class Thing
{
public event ComputerEventHandler Started;
public void OnStarted(Computer computer)
{
if (Started != null)
Started(this, new ComputerEventArgs {Computer = computer});
}
}
You must test for null in case no delegates have been added to the event. When you make the method call however all delegates which have been added will be called. This is why for events the return type is void - there is no single return value - so to feed back information you would have properties on the EventArgs which the event handlers would alter.
Another refinement would be to use the generic EventHandler delegate rather than declaring a concrete delegate for each type of args.
public class Thing
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerErrorEventArgs> Error;
}
Thank you all so much for your answers! Finally I'm starting to understand what is going on. Just one thing; It seems that if each event had a different number/type of arguments I'd need to create a different :: EventArgs class to deal with it:
public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);
This would require three classses to deal with the events!? (Well two custom, and one using the default EventArgs.Empty class)
Cheers!
Ok, FINAL clarification!: So this is pretty much the best I can do code-wise to implement those events?
public class Computer {
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler Reset;
public event EventHandler<BreakPointEvent> BreakPointHit;
public event EventHandler<ExceptionEvent> Error;
public Computer() {
Started = delegate { };
Stopped = delegate { };
Reset = delegate { };
BreakPointHit = delegate { };
Error = delegate { };
}
protected void OnStarted() {
Started(this, EventArgs.Empty);
}
protected void OnStopped() {
Stopped(this, EventArgs.Empty);
}
protected void OnReset() {
Reset(this, EventArgs.Empty);
}
protected void OnBreakPointHit(int breakPoint) {
BreakPointHit(this, new BreakPointEvent(breakPoint));
}
protected void OnError(System.Exception exception) {
Error(this, new ExceptionEvent(exception));
}
}
}