Why use events for what I can do with Delegates? - c#

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

Related

How to pass event to another event

I want pass event to another event,now I use function to do that.
Can C# pass event like b.WriteEvent += a.WriteEvent ?
If I had a lot class,and just want pass argument to above class.
I want write like : a.event += b.event. b.event += c.event
Instead of a lot no use method.
Thanks.
class Program
{
static void Main(string[] args)
{
ClassA a = new ClassA();
ClassB b = new ClassB();
a.WriteEvent += MainWrite;
b.WriteEvent += a.WireFunction; // Now I use
//b.WriteEvent += a.WriteEvent; <= Can I use like this ?
b.WireFunction("some str");
Console.ReadLine();
}
static void MainWrite(string str)
{
Console.WriteLine(str);
}
}
class ClassA
{
public event Handler WriteEvent;
public void WireFunction(string str)
{
WriteEvent(str);
}
}
class ClassB
{
public event Handler WriteEvent;
public void WireFunction(string str)
{
WriteEvent(str);
}
}
public delegate void Handler(string str);
Fact:You cannot provide an event that subscribes to an event.
All delegates (events, actions or funcs) ar multicast delegates in C#.
That means you can subscribe to an event multiple times.
In order to subscribe to an event you have to provide an action or a function. (I use the term function instead of method because we may provide a lambda)
What follows is snipped that subscribes all subscribers of Event1 to Event2.
I believe this is what you intend to do.
public class SomeClass
{
public event EventHandler Event1;
public event EventHandler Event2;
public SomeClass()
{
Event1 += Subscriber1;
Event1 += Subscriber2;
var subscribers = Event1.GetInvocationList();
if(subscribers != null)
{
foreach(var subscriber in subscribers)
{
EventHandler realSubscriber = (EventHandler)subscriber;
Event2 += realSubscriber;
}
}
Event1(this, EventArgs.Empty);
Event2(this, EventArgs.Empty);
}
public void Subscriber1(object sender, EventArgs e)
{
Console.WriteLine("Subscriber 1 invoked");
}
public void Subscriber2(object sender, EventArgs e)
{
Console.WriteLine("Subscriber 2 invoked");
}
}
Creating an instance of the SomeClass will print:
Subscriber 1 invoked
Subscriber 2 invoked
Subscriber 1 invoked
Subscriber 2 invoked
EDIT:
I tried to move the logic to an extension method and also to a normal utility method. Both did not work very well because events are null when they have no subscribers. Passing an event without subscribers would then result in the same behaviour as if null was passed. For now, this is the best I could come up with.

Events and delegates - new event for different method output

Just going through understanding events and delegates and I have a question regarding outputting a different string using the same event and delegate and if it possible. Take this example.
class Program
{
static void Main(string[] args)
{
Publisher b = new Publisher();
Subscriber a = new Subscriber(b);
b.OnLog("12");
}
}
class Publisher
{
public delegate void LogHandler(string message);
public event LogHandler Log;
public void OnLog(string message)
{
//Fire the event - notifying all subscribers
if (Log != null)
Log(message);
}
}
class Subscriber
{
public Subscriber(Publisher pub)
{
//Where B is used - subscribe to it's public event
pub.Log += new Publisher.LogHandler(HandleSomethingHappening);
pub.Log += new Publisher.LogHandler(HandleSomethingHappeningStop);
}
public void HandleSomethingHappening(string message)
{
Console.WriteLine(message);
}
public void HandleSomethingHappeningStop(string message)
{
Console.WriteLine(message);
Console.ReadLine();
}
}
Here, both methods
HandleSomethingHappening and HandleSomethingHappeningStop will output "12". If I wanted handleSomethingHappeningStop to output, lets say "24", do I need to create a new event associated with the same delegate to do this?
so for example
pub.LogOutput24 += new Publisher.LogHandler(HandleSomethingHappeningStop);
Thanks

What does SetValue do in this particular event?

I am new to events and I am trying to see how an event works and what purpose they would have. The code below is some code that a friend helped me with that we found on the net. It works but I am not certain as to how the event is used.
In the Main method there is the EventTest which creates a new object e which then intern calls SetValue method twice with a different parameter.
static void Main(string[] args)
{
EventTest e = new EventTest(5);
e.SetValue(7);
e.SetValue(11);
Console.ReadKey();
}
The following code is actually the event code.The OnNumChanged is the event that writes information if the number is changed. So when SetValue(100) is set to a different number then the event is fired.
Question: When the SetValue is set with a new value this is what cause the event to change correct?
I am just new enough to the event change that I could really use some good examples or have someone really explain this example line by line.
e.SetValue(7);
e.SetValue(11);
protected virtual void OnNumChanged()
{
if (ChangeNum != null)
{
ChangeNum();
}
else
{
Console.WriteLine("Event fired!");
}
}
Code:
class Program
{
static void Main(string[] args)
{
EventTest e = new EventTest(5);
e.SetValue(7);
e.SetValue(11);
Console.ReadKey();
}
}
public class EventTest
{
private int value;
public delegate void NumManipulationHandler();
public event NumManipulationHandler ChangeNum;
public EventTest(int n)
{
SetValue(n);
}
public void SetValue(int n)
{
if (value != n)
{
value = n;
OnNumChanged();
}
}
protected virtual void OnNumChanged()
{
if (ChangeNum != null)
{
ChangeNum();
}
else
{
Console.WriteLine("Event fired!");
}
}
}
Yes, you are correct:
public void SetValue(int n)
{
if (value != n) // check if the value actually changed
{
value = n;
OnNumChanged(); // call the event "in a save way", see below
}
}
protected virtual void OnNumChanged()
{
if (ChangeNum != null) // check if someone is listening - PROBLEM: see below
ChangeNum(); // trigger the event and call all event handlers registered
}
The Problem I mentioned above: You get the event to check it for null and then get it again to execute. In a multi-threaded environment it can happen, that between these two "gets" it changes => create a local copy:
protected virtual void OnNumChanged()
{
var changeNum = ChangeNum;
if (changeNum != null)
changeNum();
}
To actually get notified on the event you need to register for the event e.g. like this:
static void Main(string[] args)
{
EventTest e = new EventTest(5);
e.ChangeNum += () => Console.WriteLine("Num changed"); //<== this
e.SetValue(7);
e.SetValue(11);
Console.ReadKey();
}
It also would make more sense to also supply the new value in the event. That can be done by altering the delegate to:
public delegate void NumManipulationHandler(int newValue);
and call it like:
ChangeNum(value);
Then you can register like:
e.ChangeNum += newValue => Console.WriteLine("Num changed to " + newValue);
in short: yes! if the value that is passed along with the SetValue method differs from the one that was already stored, an event is fired, indicating the change as such.
Any other code that might be subscribed to such an event can then react accordingly in whatever way.
Even though I myself can learn easier from video (and I advise you to search for videos on events in C# as well), it all boils down to a way for a class to provide notifications to clients of that class when some interesting thing happens to an object (further reading here)

c# delegates and events

im trying to learn delegates and events in c#, i understand that an event is some sort of a wrapper for a delegate and a delegate is a pointer for functions/methods...
below is my code but when i run it, nothing is being shown... what could be the problems?
public class ClassHandler
{
public delegate void DoProcesses();
public event DoProcesses DoProcessesEvent;
}
public class Class1
{
public void Func1()
{
Console.WriteLine("Class 1 doing function 1");
}
public void Func2()
{
Console.WriteLine("Class 1 doing function 2");
}
}
public class Class2
{
public void Func1()
{
Console.WriteLine("Class 2 doing function 1");
}
public void Func2()
{
Console.WriteLine("Class 2 doing function 2");
}
}
class Program
{
static void Main(string[] args)
{
Class1 cs1 = new Class1();
Class2 cs2 = new Class2();
ClassHandler main = new ClassHandler();
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs1.Func1);
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs1.Func2);
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs2.Func1);
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs2.Func2);
main.DoProcessesEvent += new ClassHandler.DoProcesses(ff); // this line here is causing an error: An object reference is required for the non-static field, method, or property 'TryDelegatesAndEvents.Program.ff()'
Console.Read();
}
public void ff()
{
Console.WriteLine("gggg");
}
}
UPDATE: how do i raise the event so it will execute the methods already?
Problem with this line: main.DoProcessesEvent += new ClassHandler.DoProcesses(ff)
That is because your method ff() is a non-static method and you can't access it directly like that from a static method.
Make your method ff as static, or create and object of the containing class and assign the method with an instance of it.
For Comments: The reason you are not seeing anything is because you are just binding them to an event DoProcessesEvent, but you are not raising the event any where. You are only defining the handler for the event.
EDIT:
Change your ClassHandler class to:
public class ClassHandler
{
public delegate void DoProcesses();
public event DoProcesses DoProcessesEvent;
public void OnDoProcessEvent()
{
if (DoProcessesEvent != null)
DoProcessesEvent();
}
}
In your Main method before Console.Read(); Type:
main.OnDoProcessEvent();
This will raise the event and it will handled from the application and will give you the following output.
Class 1 doing function 1
Class 1 doing function 2
Class 2 doing function 1
Class 2 doing function 2
gggg
change main.DoProcessesEvent += new ClassHandler.DoProcesses(ff); to main.DoProcessesEvent += new ClassHandler.DoProcesses(new Program().ff); or make ff static
Well it does not compile due to the line:
main.DoProcessesEvent += new ClassHandler.DoProcesses(ff);
The error VS spits out is that:
An object reference is required for the non-static field, method, or property 'ConsoleApplication2.Program.ff()'
Just change your ff() method to be static to get around it.
Eg:
public static void ff()
{
Console.WriteLine("gggg");
}
Besides the problem pointed out in earlier comments, You have to trigger the event.
make a copy of an event before you check it for null and fire it. This will eliminate a potential problem with threading where the event becomes null at the location right between where you check for null and where you fire the event:
// Copy the event delegate before checking/calling
EventHandler copy = DoProcessesEvent ;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
This will ensure that your event fires and you will get the result.
Just to add to #Habib's answer, it would be fairly unusual to subscribe instance class methods as event handlers of an object potentially in another scope (e.g. what happens if Class1 goes out of scope, yet main() still has a subscription?). A more common scenario would be to subscribe (and de-subscribe) handlers in the same scope, often in an asynchronous manner (the below events are still raised synchronously).
namespace ConsoleApplication1
{
public delegate void ProcessCompletedEvent(string description);
public class Class1
{
public void Func1()
{
// Do Func1 work
Thread.Sleep(500);
RaiseEvent("Func1 completed");
}
public void Func2()
{
// Do Func2 work
Thread.Sleep(1000);
RaiseEvent("Func2 completed");
}
private void RaiseEvent(string description)
{
if (ProcessCompleted != null)
{
ProcessCompleted(description);
}
}
public event ProcessCompletedEvent ProcessCompleted;
}
class Program
{
static void Main(string[] args)
{
Class1 cs1 = new Class1();
// Wire up event handler
cs1.ProcessCompleted += new ProcessCompletedEvent(MyHandler);
cs1.Func1();
cs1.Func2();
Console.Read();
// Remove the subscription
cs1.ProcessCompleted -= MyHandler;
}
// *** Is in the same scope as main, which subscribes / desubscribes
public static void MyHandler(string description)
{
Console.WriteLine(description);
}
}
}

C# event handling (compared to Java)

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

Categories

Resources