Consider the following constellation.
public delegate void BarHandler(Foo sender, FooEventArgs<Object> args);
public delegate void BarHandler<T>(Foo<T> sender, FooEventArgs<T> args);
public interface Foo
{
Object Value
{ get; }
event BarHandler BarEvent;
void Update();
}
public interface Foo<T> : Foo
{
new T Value
{ get; }
new event BarHandler<T> BarEvent;
}
public class Baz<T> : Foo<T>
{
Object Foo.Value
{ get { return Value; } }
public T Value
{ get; set; }
private BarHandler handler;
event BarHandler Foo.BarEvent
{
add{ handler += value; }
remove{ handler -= value; }
}
public event BarHandler<T> BarEvent;
public void Update()
{
BarEvent(this, new FooEventArgs<T>());
(this as Foo).BarEvent(this, new FooEventArgs<Object>());
}
}
I have a Interface and a Generic Interface which extends the first Interface and a class that extends the generic Interface. The generic interface hides the not generic one, via the new keyword.
The Update method should raise both, the not-generic and the generic one. And that is the problem I am dealing with at the moment.
The resulting error is:
The event BarEvent can only appear on the left hand side of += or -= when used outside of Foo.
But I am in Foo, or do I miss something?
So, what I want is, regardless on which event the client has been registerd, it should be notified. I also should mention, that both, add and remove must work, so there is no option with delegates or something like that.
Just use the private handler variable, this should do the trick.
public void Update()
{
BarEvent(this, new FooEventArgs<T>());
handler(this, new FooEventArgs<Object>());
}
It would probably also be a good idea to check BarEvent and handler for null.
Related
I would like to create event actions to notify other classes when something happened. So my current flow looks like this
For testing purposes I created this code
Program.cs
Instantiate the first class and call a method from it (constructor is fine).
internal class Program
{
private static void Main(string[] args)
{
First f = new First();
}
}
First.cs
Instantiate the second class and call a method from it (constructor is fine). Listen for an event of the second class when some data has changed.
internal class First
{
public First()
{
// ...
Second s = new Second();
s.Updated += OnSecondUpdated;
}
private void OnSecondUpdated()
{
Console.WriteLine("Done");
Console.ReadLine();
}
}
Second.cs
Instantiate the third class and call a method from it (constructor is fine). Listen for an event of the third class when some data has changed and raise the own one.
internal class Second
{
public event Action Updated;
public Second()
{
// ...
Third t = new Third();
t.Updated += OnThirdUpdated;
}
private void OnThirdUpdated()
{
// ...
Updated();
}
}
Third.cs
Raise an event when some data has changed.
internal class Third
{
public event Action Updated;
public Third()
{
// ...
Updated();
}
}
Unfortunately the event variables are null. How can I instantiate these variables properly?
The problem here is that you're trying to do this in the constructor, where at that time nothing has (yet) been assigned to the Updated event. You can "solve" this by checking for null:
internal class Third
{
public event Action Updated;
public Third()
{
// ...
if(Updated != null)
Updated();
}
}
But it wont mean your code now "works" as you only assign the event a handler after constructor has been called:
Third t = new Third();
t.Updated += OnThirdUpdated;
So one possible solution for this pattern is to NOT do this raising of the event in the constructor, and instead defer the logic to another method.
internal class Third
{
public event Action Updated;
public Third()
{
}
public void Init()
{
// ...
if(Updated != null)
Updated();
}
}
Third t = new Third();
t.Updated += OnThirdUpdated;
t.Init();
You call the Update() before the classes can subscribe to the events, due to the constructor of the underlying object being called first. I changed it so that the constructor takes the related class and subscribes the event itself.
internal class First
{
public First()
{
Second s = new Second(this);
}
internal void OnSecondUpdated()
{
Console.WriteLine("Done");
Console.ReadLine();
}
}
internal class Second
{
public event Action Updated;
public Second(First f)
{
Updated += f.OnSecondUpdated;
Third t = new Third(this);
}
internal void OnThirdUpdated()
{
Updated();
}
}
internal class Third
{
public event Action Updated;
public Third(Second s)
{
Updated += s.OnThirdUpdated;
Updated();
}
}
I will admit, i am doing homework and i am stuck on this one question (Part A). How do i pass the notice method as reference to the railway signal ? Can't i just find out which class was called in the abstract constructor and then print the class name within the notify method? For example:
RailwayUser
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.Writeline(className);
}
RailwaySignal Class
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
This kind of code / design is flawed, since what it does is RailwayUser, registers the object reference with the _watchers List in the RailWaySignal class, which in turn calls the public Notice method on each user when Notify is invoked, which is not how Event Signaling or Function Pointer works. In fact public _watchers is dangerous, as it can be cleared by any user, though that can be moderated using property access
Code with Issue
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
Following shall be the actual code using events and delegates:
Correct Version
Code Snippet Online - https://www.jdoodle.com/embed/v0/uEc
void Main()
{
List<RailwayUser> railwayUsers = new List<RailwayUser>();
railwayUsers.Add(new RailwayUser());
railwayUsers.Add(new RailwayUser());
RailwayUser.TestNotification();
}
public enum Colour
{
Red,
Green,
NoSignal
}
public class RailwaySignal
{
public string Name {get; set;}
public RailwaySignal(string railwaySignalName)
{
Name = railwaySignalName;
}
// Delegate for handling event
public delegate void RailwaySignalEventHandler(object source, Colour e);
// Delagate object for handling event
private RailwaySignalEventHandler _railwaySignalEvent;
// Event Accessor
public event RailwaySignalEventHandler RailwaySignalEvent
{
add
{
lock (this)
{
_railwaySignalEvent += value;
}
}
remove
{
lock (this)
{
_railwaySignalEvent -= value;
}
}
}
// Invoke Event for subscribed clients
private void Notify()
{
if (_railwaySignalEvent != null)
_railwaySignalEvent.Invoke(this, Colour.Green);
}
// Test the Event Invocation
public void TestEvent()
{
Notify();
}
}
public class RailwayUser
{
private static RailwaySignal railwaySignal { get; set;} = new RailwaySignal("Signal1");
public RailwayUser()
{
railwaySignal.RailwaySignalEvent += this.Notice;
}
public static void TestNotification()
{
railwaySignal.TestEvent();
}
public void Notice(object sender, Colour color)
{
Console.WriteLine($"Notice Called, Colour is :: {color}, Sender is :: {((RailwaySignal)sender).Name}");
}
}
Result
Notice Called, Colour is :: Green, Sender is :: Signal1
Notice Called, Colour is :: Green, Sender is :: Signal1
Important Details
Signature of the event is, (object source, Colour e) which helps in passing the relevant information across to the RailwayUser called, We now know the RailwaySignal triggering the notification to the RailwayUser and its Colour value
Event / Delegate has same signature as called method (which is the basis of working of Delegate / function pointers)
For simplification RailwayUser is a non abstract class
Event is executed using Notify() method inside the RailwaySignal, we are calling it artificially using TestNotification() inside RailwayUser just for demo purpose, but ideally it shall be internally triggered and shall pass on current state like Colour
Pre-defined delegates like Func, Action are quite often used for similar notification mechanism, They internally works using similar mechanism, though declaring an explicit event which is internally a delegate is a well defined pattern, especially for the Ui controls
Standard events exposed by the .Net framework have the signature object sender, EventArgs e, where EventArgs can wrap all information from Event executor (RailwaySignal) to Event receiver (RailwayUser)
It seem like a Observer pattern.You can pass SubClass which inherit from RailwayUser object instance into RailwaySignal class
Your RailwayUser class need create public abstract void Notice(Colour state) method.
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
Driver class can inherit RailwayUser class then override Notice method.
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine($"Driver see the {state.ToString()}");
}
}
There are
List<RailwayUser> _watches contain observable object
use SubScript(RailwayUser user) subscription user on _watches List.
RailwayUser Notify() to invoke all your observable Notify method.
look like this.
public class RailwaySignal
{
private List<RailwayUser> _watches;
public Colour Stata { get; set; }
public RailwaySignal()
{
_watches = new List<RailwayUser>();
}
public void SubScript(RailwayUser user)
{
_watches.Add(user);
}
public void Notify()
{
foreach (RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(Stata);
}
}
}
sample:https://dotnetfiddle.net/GcdGMy
You can also use event to pass method into RailwaySignal then invoke Notify method.
public enum Colour
{
Green,
Red,
Disable
}
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Driver see the "+ state.ToString());
}
}
public class Controller : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Controller see the " + state.ToString());
}
}
public class RailwaySignal
{
public delegate void NoticeEvent(Colour state);
public event NoticeEvent Notifys;
public Colour Stata { get; set; }
public void Notify()
{
if (Notifys != null)
{
Notifys(Stata);
}
}
}
use like this.
RailwaySignal railway = new RailwaySignal() { Stata = Colour.Green};
railway.Notifys += new Driver().Notice;
railway.Notifys += new Controller().Notice;
railway.Notify();
sample : https://dotnetfiddle.net/GcdGMy
I want an abstract class that raises an event, this event will be raised by the concrete class.
What I want is when I use another class to listen to these events the signature of the delegate should have the concrete type not the abstract, I don't want to cast it.
For the moment I have come up with this solution. It works but I don't find it particularly clever especially because of the "STUPID, DOESN'T MAKE SENSE......" part.
Here is my solution :
public delegate void ClassAEventHandler<TClassA>(TClassA classA) where TClassA : ClassA;
//Abstract class that raise Event
public abstract class ClassA<TClassA> : where TClassA : ClassA
{
public event ClassAEventHandler<TClassA> onClassEventRaised;
private TClassA eventClassA;
public void registerEventClass(TClassA classA)
{
this.eventClassA = classA;
}
public void raiseClassEvent()
{
this.onClassEventRaised(this.eventClassA);
}
}
// Exemple of concrete type
public class ClassB : ClassA<ClassB> // <------ IT SEEMS DUMB
{
public void action()
{
//Do something then raise event
this.raiseClassEvent();
}
public void saySomething() {};
}
// Exemple of concrete type
public class ClassC : ClassA<ClassC> // <------ IT SEEMS DUMB
{
public void command()
{
//Do something then raise event
this.raiseClassEvent();
}
public void destroySomething() {};
}
//Class that listen to the event raised
public class MyEventListener
{
private ClassB classB;
private ClassC classC;
public MyEventListener()
{
this.classB = new ClassB();
this.classB.registerEventClass(this.classB); // <------ STUPID, DOESN'T MAKE SENSE......
this.classB.onClassEventRaised += classB_onClassEventRaised;
this.classC = new ClassC();
this.classC.registerEventClass(this.classC); // <------ STUPID, DOESN'T MAKE SENSE......
this.classC.onClassEventRaised += classC_onClassEventRaised;
}
public void classB_onClassEventRaised(ClassB classB)
{
classB.saySomething();
}
public void classC_onClassEventRaised(ClassC classC)
{
classC.destroySomething();
}
//What i don't want
/*
public void classB_onClassEventRaised(ClassA classA)
{
((classB)classA).saySomething();
}
*/
}
First of all, you're not following regular event design in .NET.
Instead of implementing your own delegate, use EventHandler<TArgs>, and create a derived class of EventArgs.
Your CustomEventArgs should have a T generic parameter:
public class CustomEventArgs<T> where T : A
{
private readonly T _instance;
public CustomEventArgs(T instance)
{
_instance = instance;
}
public T Instance { get { return _instance; } }
}
Also, don't implement a custom way of registering events. If you want to encapsulate how handlers are added to the event, you need to use event accessors.
Finally, you could implement your classes as follows:
public class A<T> where T : A
{
private event EventHandler<CustomEventArgs<T>> _someEvent;
// An event accessor acts like the event but it can't be used
// to raise the event itself. It's just an accessor like an special
// event-oriented property (get/set)
public event EventHandler<CustomEventArgs<T>> SomeEvent
{
add { _someEvent += value; }
remove { _someEvent -= value; }
}
protected virtual void RaiseSomeEvent(CustomEventArgs<T> args)
{
// If C# >= 6
_someEvent?.Invoke(this, args);
// Or in C# < 6
// if(_someEvent != null) _someEvent(this, args);
}
}
public class B : A<B>
{
public void DoStuff()
{
// It's just about raising the event accessing the whole
// protected method and give an instance of CustomEventArgs<B>
// passing current instance (i.e. this) to CustomEventArgs<T>
// constructor.
RaiseSomeEvent(new CustomEventArgs<B>(this));
}
}
Now, if you try to handle SomeEvent, you'll get the CustomEventArgs<B> typed as B instead of A:
B b = new B();
b.SomeEvent += (sender, args) =>
{
// args.Instance is B
B instance = args.Instance;
};
b.DoStuff(); // Raises SomeEvent internally
I have the following scenario:
public abstract class SomeBaseClass
{
public event EventHandler SomeEvent;
...
}
public class SomeClass : SomeBaseClass
{
public void DoSomething()
{
//TODO
if (SomeEvent != null)
SomeEvent(this, EventArgs.Empty);
}
}
SomeBaseClass has an event which needs to be called in a base class, however this it isn't possible to directly call the event from a base class. To get around this, I can override the event in the base class, like so:
public class SomeClass : SomeBaseClass
{
new public event EventHandler SomeEvent;
This is fine I guess, however my question is whether there is some kind of universal method, or good practice for implementing the functionality above?
The fact that it isn't possible to call an event from a base class suggests that I shouldn't really be doing this in the first place, perhaps the responsibility of calling the event should be only in SomeBaseClass?
That isn't allowed indeed. If I may recommend an alternative approach:
public abstract class SomeBaseClass
{
public event EventHandler SomeEvent;
protected void RaiseSomeEvent(EventArgs e)
{
var eh = SomeEvent;
if (eh != null)
eh(this, e);
}
}
public class SomeClass : SomeBaseClass
{
public void DoSomething()
{
//TODO
RaiseSomeEvent(EventArgs.Empty);
}
}
Note that I have moved the invocation of the event handler to the owning class, this is required by .NET / C# since only that class can invoke the event handler. Second, I have made the event handler thread safe by assigning it to eh first.
Never hide the base class' event by using the new keyword! You will get unexpected results when you use the base class' type as type for a variable or when the base class invokes the event.
I would stay away from using new mainly because code will behave differently if an object is cast to the base class. Here's an alternative implementation:
public abstract class SomeBaseClass
{
public virtual event EventHandler SomeEvent;
protected virtual void HandleSomeEvent()
{
var ev = SomeEvent; // Localize event field used
if (ev != null)
{
ev(this, EventArgs.Empty);
}
}
}
public class SomeClass : SomeBaseClass
{
public override event EventHandler SomeEvent
{
add { base.SomeEvent += value; }
remove { base.SomeEvent -= value; }
}
protected override void HandleSomeEvent()
{
base.HandleSomeEvent();
// ... My own code here
}
}
This allows for a great deal of flexibility. You can provide some implementation of event handling as well as allow the implementer to completely override the base class implementation.
public delegate void ErrorHandler(string result);
public class BaseClass
{
public event ErrorHandler OnError;
protected void RaiseErrorEvent(string result)
{
OnError?.Invoke(result);
}
}
public class SampleClass:BaseClass
{
public void Error(string s)
{
base.RaiseErrorEvent(s);
}
}
I personally prefer to use delegates for that :
public abstract class SomeBaseClass
{
public event EventHandler SomeEvent;
protected Action<object, EventArgs> SomeEventInvoker;
public SomeBaseClass()
{
SomeEventInvoker = new Action<object, EventArgs>((sender, args) =>
{ if (SomeEvent != null) SomeEvent(sender, args); });
}
}
public class SomeClass : SomeBaseClass
{
public SomeClass()
{
DoSomething();
}
public void DoSomething()
{
SomeEventInvoker(this, new EventArgs());
}
}
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));
}
}
}