I have a simple delegate, event and property allowing me to create callback subscriptions on events:
public static class Test
{
/// <summary>Delegate for property changed event</summary>
public delegate void TestEventHandler();
/// <summary>Event called when value is changed</summary>
public static event TestEventHandler OnTestHappening;
/// <summary>Property to specify our test is happening</summary>
private static bool testHappening;
public static bool TestHappening
{
get
{
return testHappening;
}
set
{
testHappening = value;
// Notify our value has changed only if True
// ie. Only fire an event when we're ready as we'll hook methods to the Event that must only fire if ready
if ( value )
{
if ( OnTestHappening != null )
OnTestHappening();
}
}
}
}
I can then easily subscribe and unsubscribe to the event and fire the event callbacks as needed:
public class Tester
{
private void Main()
{
Testing();
// Start the test
Test.TestHappening = true;
}
private void Testing()
{
// Unsubscribe from event
Test.OnTestHappening -= Testing;
// Check if we're busy testing yet
if ( !Test.TestHappening )
{
// Subscribe to event
Test.OnTestHappening += new Test.TestEventHandler( Testing );
return;
}
// Do stuff here....
}
}
When compiling, code analysis gives me, "CA1009: Declare event handlers correctly?" and I've searched high and low and found many questions, articles etc but none that feel like they address my scenario. I can't seem to find a concrete starting point for the conversion and I'm starting to wonder if I'm meant to completely rewrite the implementation?
Edit: Firstly I really appreciate the assists, I did look carefully through all the sites I could before posting this and I did see (and try work with) each of the links that you all posted. I even went back and studied delegates and events again but I feel like I'm missing the starting point somehow because each time I try change a part of it, I just keep producing errors that I can't come back from like:
public delegate void TestEventHandler( object sender, EventArgs e );
With the other links I visited, I could only find 1 similarity to my code (either in the delegate, the handler or the property) but couldn't find anything that related enough to mine to actually instil that "eureka" moment
Edit 2: I have now rebuilt my example with what "looks" to be the correct standard but this code is so fugly it looks like it was beaten with a confogulus stick and dipped in a tank of confutious before being deep fried in horriduculous:
public static class Test
{
/// <summary>Delegate for property changed event</summary>
public delegate void TestEventHandler( object sender, EventArgs e );
/// <summary>Event called when value is changed</summary>
public static event TestEventHandler OnTestHappening;
/// <summary>Property to specify our test is happening</summary>
private static bool testHappening;
public static bool TestHappening
{
get
{
return testHappening;
}
set
{
testHappening = value;
// Notify our value has changed only if True
// ie. Only fire an event when we're ready as we'll hook methods to the Event that must only fire if ready
if ( value )
{
if ( OnTestHappening != null )
OnTestHappening( null, EventArgs.Empty );
}
}
}
}
public class Tester
{
private void Main()
{
Testing( this, EventArgs.Empty );
// Start the test
Test.TestHappening = true;
}
private void Testing( object sender, EventArgs e )
{
// Unsubscribe from the event
Test.OnTestHappening -= Testing;
// Check if we're busy testing yet
if ( !GlobalClass.SystemOnline )
{
// Subscribe to the event
Test.OnTestHappening += new Test.TestEventHandler( Testing );
return;
}
// Do stuff here....
}
}
Please tell me I've missed something and that there is in fact a more elegant implementation
Edit 3 : Based on the code by Enigmativity, I've reworked the code to it's most basic form. I've also moved the code setting the variable to true in a different method so it doesn't look so daft sitting in Main.
public static class Test4
{
/// <summary>Event called when value is changed</summary>
public static event EventHandler TestHappening;
/// <summary>Property to specify our test is happening</summary>
private static bool test = false;
public static bool Test
{
get
{
return test;
}
set
{
// Notify our value has changed only if True
// ie. Only fire an event when we're ready as we'll hook methods to the Event that must only fire if ready
if ( value )
{
TestHappening( null, EventArgs.Empty );
}
}
}
}
public class Tester4
{
private void Main()
{
Testing( this, EventArgs.Empty );
}
private void Testing( object sender, EventArgs e )
{
// Unsubscribe from the event
Test4.TestHappening -= Testing;
// Check if we're busy testing yet
if ( !Test4.Test )
{
// Subscribe to the event
Test4.TestHappening += Testing;
return;
}
// Do stuff here....
}
private void SomeMethodCalledFromSomewhere()
{
// Set the value to true and thereby start the test
Test4.Test = true;
}
}
Would this be considered good code or should I rather have the OnTestHappening method as defined in Enigmativity's code?
Why can't I use a parameterless delegate? It's now using the default ( object sender, EventArgs e ) but that feels overkill and doesn't make sense why the compiler is happy with it but according to coding standards it's considered bad code? I'm not arguing the standard but rather trying to understand it's reasoning.
As per Storm's request, here is how I would most likely structure the code. It's more inline with the standard conventions.
public static class TestClass
{
public delegate void TestEventHandler(object sender, EventArgs e);
public static event TestEventHandler TestHappening;
private static bool test = false;
public static bool Test
{
get
{
return test;
}
set
{
test = value;
if (test)
{
OnTestHappening();
}
}
}
private static void OnTestHappening()
{
var handler = TestHappening;
if (handler != null)
handler(null, EventArgs.Empty);
}
}
And Tester would look like this:
public class Tester
{
public void Main()
{
TestClass.TestHappening += Testing;
Go();
}
private void Testing(object sender, EventArgs e)
{
Console.WriteLine(TestClass.Test);
TestClass.TestHappening -= Testing;
}
private void Go()
{
TestClass.Test = true;
}
}
Calling it would look like this:
var tester = new Tester();
tester.Main();
Running this outputs True to the console.
If I were writing this in a more standard way, it would look like this:
public class TestEventArg : EventArgs
{
public TestEventArg(bool updatedValue)
{
this.UpdatedValue = updatedValue;
}
public bool UpdatedValue { get; private set; }
}
public class TestClass
{
public event EventHandler<TestEventArg> TestHappening;
private bool test = false;
public bool Test
{
get { return test; }
set
{
var old = test;
test = value;
if (test != old)
OnTestHappening(test);
}
}
private void OnTestHappening(bool updatedValue)
{
var handler = TestHappening;
if (handler != null)
handler(this, new TestEventArg(updatedValue));
}
}
Related
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)
I've an application where I'm not able to remove event handlers because I don't know when the last reference will be freed.
My application contains a PropertyChanged event source that is put into a container class that also implements INotifyPropertyChanged. This hierarchy contains more than 6 levels. Each instance of a level could be placed into multiple other instances. That's the reason why I couldn't determine when to free those instances.
The instances on the lowest level will live for the whole application runtime. This causes that all other instances will not be freed and I got a memory leak.
To avoid this event driven memory leak I tried to use WeakEventManager(TEventSource, TEventArgs). This class is only available in .Net 4.5 and because of compatibility to existing hardware I’ve to use .Net 4.0.
In .Net 4.0 is a PropertyChangedEventManager available that should do the same for INotifyPropertyChanged.
My classes are freed correctly.
But there is still a memory leak.
I simplified my application to the following code that produces a memory leak:
// This code will force the memory leak
while (true)
{
var eventSource = new StateChangedEventSource();
var eventReceiver = new StateChangedEventReceiver();
PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
}
public class EventSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class EventReceiver : IWeakEventListener
{
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return true;
}
}
Yes I know there is no RemoveListener call. I couldn’t determine when an instance is never used and could be freed. If I knew that I could use normal event registration and deregistration. In that case I don’t have to use the PropertyChangedEventManager.
What is the problem of my sample code? Why does it produce a memory leak?
Edit 2014/02/17:
I tried the WeakEventManager(TEventSource, TEventArgs) and .Net 4.5 and the problem still exists.
var eventSource = new EventSource();
var i = 0;
while (true)
{
var eventReceiver = new EventReceiver();
// --> Use only one of the following three lines. Each of them will produce a memory leak.
WeakEventManager<EventSource, PropertyChangedEventArgs>.AddHandler(eventSource, "PropertyChanged", eventReceiver.OnEvent);
PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
WeakEventManager<EventSource, EventArgs>.AddHandler(eventSource, "SomeOtherEvent", eventReceiver.OnSomeOtherEvent);
// <--
++i;
if (i == 1 << 18)
{
Thread.Sleep(10);
GC.Collect(2);
Thread.Sleep(10);
i = 0;
}
}
public class EventSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<EventArgs> SomeOtherEvent;
}
public class EventReceiver : IWeakEventListener
{
public void OnSomeOtherEvent(object sender, EventArgs args)
{
}
public void OnEvent(object sender, PropertyChangedEventArgs args)
{
}
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return true;
}
}
This code compiled using .Net 4.5 do also run out of memory. I got the hint using the Thread.Sleep construct here.
I don't think the isue with WeakEventManager<,> is specific to non-WPF, as i can reproduce a memory leak in a WPF application as well.
The problem lies with the management of the event table. For each subscription, the WeakEventManager creates an entry in a table. This entry and table are (by necessity) strong-referenced.
The problem is, that by default, the WeakEventManager does not clean up the records. You have to call RemoveHandler. But beware. It's not thread safe. If you call it from another thread it may fail (doesn't throw an exception, you'll just experience that there's still a memory leak). When called from a finalizer, it doesn't work reliably either.
I also investigated into the source code and found that while it contains logic to do cleanup on AddHandler and when an event is received, it is disabled by default (see WeakEventManager.cs => WeakEventTable.CurrentWeakEventTable.IsCleanupEnabled). Also, you can't access the Cleanup method since the methods and properties necessary to do so are private or internal. So you can't even create subclass to access these methods / modify the behavior.
WeakEventManager<,> is broken
So basically (as far as i can understand) WeakEventManager<,> is broken by design (it keeps a StrongReference to the subscriber table-entry).
Instead of fixing a MemoryLeak it will only reduce the MemoryLeak (the event source and listener can be garbage collected, but the entry for the event subscription is not => new memory leak). Of course the memory leak introduced by WeakEventManager<,> is small.
Based on the information at msdn and codeproject I realized that the WeakEventManager(TEventSource, TEventArgs) class will only work in WPF applications. I'm using WinForms what's the reason why it seems not to work.
I decided to create my own WeakEventManager that works without using the build in WeakEventManager provided by the .Net framework.
The implementation of my WeakEventManager uses a background thread to clean up all instances. Maybe there's a better solution but this solution will work correctly in my application.
public static class ThreadedWeakEventManager
{
private static readonly TimeSpan CleanupInterval = TimeSpan.FromSeconds(1.0);
private static readonly List<IInternalWeakEventManager> EventManagers = new List<IInternalWeakEventManager>();
private static volatile bool _performCleanup = true;
static ThreadedWeakEventManager()
{
new Thread(Cleanup) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
}
public static void AddHandler<TEventArgs>(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
where TEventArgs : EventArgs
{
var weakEventManager = new InternalWeakEventManager<TEventArgs>(eventSource, eventName, eventHandler);
lock (EventManagers)
{
EventManagers.Add(weakEventManager);
}
}
public static void AddPropertyChangedHandler(INotifyPropertyChanged eventSource, EventHandler<PropertyChangedEventArgs> eventHandler)
{
AddHandler(eventSource, "PropertyChanged", eventHandler);
}
public static void AddCollectionChangedEventHandler(INotifyCollectionChanged eventSource, EventHandler<NotifyCollectionChangedEventArgs> eventHandler)
{
AddHandler(eventSource, "CollectionChanged", eventHandler);
}
public static void RemoveHandler<TEventArgs>(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
where TEventArgs : EventArgs
{
if (eventSource == null || string.IsNullOrWhiteSpace(eventName) || eventHandler == null)
{
return;
}
lock (EventManagers)
{
EventManagers.RemoveAll(item => object.ReferenceEquals(item.EventData.EventSource, eventSource) && item.EventName.Equals(eventName) && eventHandler.Method.Equals(item.EventData.EventHandlerMethodInfo));
}
}
public static void RemovePropertyChangedHandler(INotifyPropertyChanged eventSource, EventHandler<PropertyChangedEventArgs> eventHandler)
{
RemoveHandler(eventSource, "PropertyChanged", eventHandler);
}
public static void RemoveCollectionChangedEventHandler(INotifyCollectionChanged eventSource, EventHandler<NotifyCollectionChangedEventArgs> eventHandler)
{
RemoveHandler(eventSource, "CollectionChanged", eventHandler);
}
public static void CancelCleanup()
{
_performCleanup = false;
}
private static void Cleanup()
{
while (_performCleanup)
{
Thread.Sleep(CleanupInterval);
lock (EventManagers)
{
for (var i = EventManagers.Count - 1; i >= 0; --i)
{
var item = EventManagers[i];
if (item.EventData.IsGarbageCollected)
{
item.UnwireEvent();
EventManagers.RemoveAt(i);
}
}
}
}
}
private interface IInternalWeakEventManager
{
string EventName { get; }
IWeakEventData EventData { get; }>
void UnwireEvent();
void OnEvent(object sender, EventArgs args);
}
private class InternalWeakEventManager<TEventArgs> : IInternalWeakEventManager
where TEventArgs : EventArgs
{
private static readonly MethodInfo OnEventMethodInfo = typeof(InternalWeakEventManager<TEventArgs>).GetMethod("OnEvent");
private EventInfo _eventInfo;
private Delegate _onEvent;
public InternalWeakEventManager(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
{
this.EventData = new WeakEventData<TEventArgs>(eventSource, eventHandler);
this.WireEvent(eventSource, eventName);
}
public string EventName
{
get { return this._eventInfo.Name; }
}
public IWeakEventData EventData { get; private set; }
public void UnwireEvent()
{
var eventSource = this.EventData.EventSource;
if (eventSource == null)
{
return;
}
this._eventInfo.RemoveEventHandler(eventSource, this._onEvent);
}
public void OnEvent(object sender, EventArgs args)
{
this.EventData.ForwardEvent(sender, args);
}
private void WireEvent(object eventSource, string eventName)
{
this._eventInfo = eventSource.GetType().GetEvents().FirstOrDefault(item => item.Name == eventName);
if (this._eventInfo == null)
{
throw new InvalidOperationException(string.Format("The event source type {0} doesn't contain an event named {1}.", eventSource.GetType().FullName, eventName));
}
this._onEvent = Delegate.CreateDelegate(this._eventInfo.EventHandlerType, this, OnEventMethodInfo);
this._eventInfo.AddEventHandler(eventSource, this._onEvent);
}
}
private interface IWeakEventData
{
bool IsGarbageCollected { get; }
object EventSource { get; }>
MethodInfo EventHandlerMethodInfo { get; }
void ForwardEvent(object sender, EventArgs args);
}
private class WeakEventData<TEventArgs> : IWeakEventData
where TEventArgs : EventArgs
{
private readonly WeakReference _eventSource;
private readonly WeakReference _eventTargetInstance;
public WeakEventData(object eventSource, EventHandler<TEventArgs> eventHandler)
{
this._eventSource = new WeakReference(eventSource);
this._eventTargetInstance = new WeakReference(eventHandler.Target);
this.EventHandlerMethodInfo = eventHandler.Method;
}
public object EventSource
{
get { return this._eventSource.Target; }
}
public MethodInfo EventHandlerMethodInfo { get; private set; }
public bool IsGarbageCollected
{
get
{
return !this._eventSource.IsAlive || !this._eventTargetInstance.IsAlive;
}
}
public void ForwardEvent(object sender, EventArgs args)
{
var target = this._eventTargetInstance.Target;
if (target != null)
{
this.EventHandlerMethodInfo.Invoke(target, new[] { sender, args });
}
}
}
}
Memory leak in WeakEventManager can occur if AddListener is called from different threads.
Just call AddListener from the main UI thread and inner "CleanUp" will work fine.
I declare a subscription to event in:
public class MainClass
{
public void btndel_bar_Click(object sender, RoutedEventArgs e)
{
SomeClass sc = new SomeClass();
sc.FieldUpdate += new SomeClass.FieldUpdateHandler(sc_FieldUpdate);
}
void sc_FieldUpdate(object sender, ValueEventArgs e)
{
MessageBox.Show(e.Smth_property);
}
}
And here is I want to listen event:
public class Someclass
{
public delegate void FieldUpdateHandler(object sender, ValueEventArgs e);
public event FieldUpdateHandler FieldUpdate;
void Somemethod()
{
string str = "Steel";
ValueEventArgs args = new ValueEventArgs(str);
FieldUpdate(this, args);
}
}
A class which carries data:
public class ValueEventArgs : EventArgs
{
private string smth;
public ValueEventArgs(string smth)
{
this.smth = smth;
}
public string Smth_property
{
get { return smth; }
}
}
I always have FieldUpdate=null. How to solve it?
You're calling Somemethod() in the constructor, before the calling code gets a chance to add the event handler.
Therefore, the event is still null.
The moment you create the object of SomeClass your event would get reinitialized.
Make your event a static so that multiple objects of SomeClass would share it
public static event FieldUpdateHandler FieldUpdate;
I've read articles about delegates and events and after reading I always I thought to make all operations again. I did all over again and it works! Consequently I done something wrong when I did at the beginning of.
I have two classes, and i have to make an event to communicate between these classes.
Class a
{
public delegate void delegat(int a);
public event delegat exit;
...
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
// My event named exit should run here, but I get exception!
exit(100);
}
}
Class b
{
a instance=new a();
a.exit+=new a.delegat(my_fun);
...
private void my_fun(int x)
{
if(x==100)
do_smth;
...
}
}
But the thing is that i get exception: "object reference not set to an instance of an object".
I can't understand what Am I doing wrong? Where should I make a new instance of this?
Thanks for help!
You are trying to assign the exit event on the class itself and not the instance e.g.
a.exit += ...
Should be:
instance.exit += ...
You also aren't checking whether your exit event has been assigned before attempting to fire it. There are other issues which you haven't taken into consideration like race conditions.
Here is a general example of a relatively safe way of handling events e.g.
public class A
{
public delegate void ExitHandler(object sender, int a);
public event ExitHandler Exit;
...
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
OnExit(100);
}
protected virtual void OnExit(int a)
{
// take a reference to the event (incase it changes)
var handler = Exit;
if (handler != null)
{
handler(this, a);
}
}
}
public class B
{
private A _a;
public B()
{
_a = new A();
_a.Exit += (sender, value) => my_fun(value);
}
private void my_fun(int x)
{
if(x==100)
do_smth;
...
}
}
I would change "class a" code for calling the event to as follows:
Class a
{
public delegate void delegat(int a);
public event delegat exit;
...
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
if (this.exit != null) // just in case a_FormClosed fires before assigning the event
exit(100);//here should run my event named exit but i get exception!
}
}
Verify if there is any subscriber exist to your event before raising it:
if (exit != null)
exit(100);
Another option - subscribing dummy event handler when you are defining event in class A:
public event delegat exit = (_) => { };
Also use PascalCase naming for types, events and methods. And there is predefined delegate in .NET which receives one argument and returns void: Action<T>
Try this
namespace foo
{
public delegate void delegat(int a);
Class a
{
public event delegat exit;
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
if(exit != null)
{
exit(100);//here should run my event named exit but i get exception!
}
}
}
}
Class b
{
a instance=new a();
instance.exit+=new delegat(my_fun);
...
priavte void my_fun(int x)
{
if(x==100)
do_smth;
...
}
}
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));
}
}
}