Error when raising custom event - c#

I have a class that will write a log. The class needs to raise an event (under specific circumstances not indicated below), that will be comsumed by a class to react on it. I have the code below but as soon as I try to raise the event, I get an error on the line as indicated, that
Object reference not set to an instance of an object
Any idea what I'm missing?
//1. Class where event is registered
public class LogEvent
{
public delegate void WriteLogEventHandler(object Sender, WriteLogEventArgs e);
public event WriteLogEventHandler WriteLog;
public class WriteLogEventArgs : EventArgs
{
public string Message { get; set; }
public WriteLogEventArgs(string message) : base()
{
Message = message;
}
}
//Raise the event.
internal void OnWriteLog(WriteLogEventArgs e)
{
WriteLog(this, e); //Error here. Seems like WriteLog is null
}
//2. Class where event is raised.
public class Logs
{
public static void WriteLog(string message)
{
LogEvent.WriteLogEventArgs args = new LogEvent.WriteLogEventArgs(message);
new LogEvent().OnWriteLog(args);
}
}
//3. Class where event should be consumed
public class MyClass()
{
private LogEvent _logEvent;
public MyClass()
{
//Subscribe to event:
_logEvent = new LogEvent();
_logEvent.WriteLog += (sender, args) => { DoSomething(args.Message); };
}
public void DoSomething(string message)
{ ... }
}

Two issues:
You're raising the event whether or not anyone has subscribed to it. Don't do that - you will get a NullReferenceException if you call WriteLog(this, e) when WriteLog is null. In C# 6 it's easy to avoid this:
WriteLog?.Invoke(this, e);
You're subscribing to the event on a different LogEvent instance than the one which is raising the event. This is more of a design issue than anything else - it makes no sense for an individual log event to have a list of subscribers. Instead, you should have a Logger or similar which has the subscribers (via an event), then each LogEvent is passed to those subscribers. You'd create one Logger, subscribe to it, then call WriteLog on the same instance.

Related

initialize event actions to raise event notifications

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

How to make an event which gets triggered in another class

In my code for the PluginManager the event PluginEvent gets triggered after
a plugin has been added. But I want to get the event also triggered in the test class.
Somehow I cant solve this problem. The event only gets triggered in the PluginManager class. I read some articles how to create events and so on, but I got even more confused
PluginManager class
public class PluginEventArgs
{
public PluginEventArgs(string s) { Text = s; }
public String Text { get; private set; } // readonly
}
public class PluginManager
{
// Declare the delegate (if using non-generic pattern).
public delegate void PluginEventHandler(object sender, PluginEventArgs e);
// Declare the event.
public event PluginEventHandler PluginEvent;
protected virtual void RaiseSampleEvent(string message)
{
if (PluginEvent != null)
PluginEvent(this, new PluginEventArgs(message));
}
public PluginManager()
{
PluginEvent += PluginManager_PluginEvent;
SomeMethod();
}
void PluginManager_PluginEvent(object sender, PluginEventArgs e)
{
//This event gets triggered =)
}
public void SomeMethod()
{
//Code
RaiseSampleEvent("Name of the Plugin");
//Code
}
}
My test class:
class test
{
public test()
{
PluginManager pluginMg = new PluginManager();
pluginMg.PluginEvent += pluginMg_PluginEvent;
}
//I want this event to get triggered when a new plugin has been found
void pluginMg_PluginEvent(object sender, PluginEventArgs e)
{
MessageBox.Show(e.Text);
}
}
How can I manage to get the event triggered in the test class?
Thanks for any advise!
You're actually doing things right except for one logical Mistake.
In your test class you're creating the PluginManager by using the constructor. The constructor of PluginManager first subscribes to the event and then raises it.
AFTERWARDS you're subscribing to that event.
The simple Problem is that when you are raising the event your test class has not subscribed yet. When you raise that event again everything should work out just fine.
Another thing is that I would use the generic EventHandler class instead of creating your own delegates. This keeps your code cleaner and everyone knows that this is meant to be an event at first glance.
Just inherit PlugInEventArgs from EventArgs and then use EventHandler.
In your PluginManager class you shouldn't subscribe to your own event PluginEvent, you should subscribe to an external event or just raise the PluginEvent.
Let me give you an example:
public class PluginEventArgs
{
public PluginEventArgs(string s) { Text = s; }
public String Text { get; private set; } // readonly
}
public class OtherClass
{
public event PluginEventHandler PluginEvent;
private void RaiseEvent()
{
if (null != PluginEvent)
PluginEvent(this, new PluginEventArgs("some message"));
}
}
public delegate void PluginEventHandler(object sender, PluginEventArgs e);
public class PluginManager
{
public event PluginEventHandler PluginEvent;
private OtherClass otherClass;
protected virtual void RaiseSampleEvent(string message)
{
if (PluginEvent != null)
PluginEvent(this, new PluginEventArgs(message));
}
public PluginManager(OtherClass otherClass)
{
this.otherClass = otherClass;
this.otherClass.PluginEvent += otherClass_PluginEvent;
SomeMethod();
}
void otherClass_PluginEvent(object sender, PluginEventArgs e)
{
if (PluginEvent != null)
PluginEvent(sender, e); // this way the original sender and args are transferred.
}
public void SomeMethod()
{
//Code
RaiseSampleEvent("Name of the Plugin");
//Code
}
}
class test
{
public test()
{
OtherClass otherClass = new OtherClass();
PluginManager pluginMg = new PluginManager(otherClass);
pluginMg.PluginEvent += pluginMg_PluginEvent;
}
//I want this event to get triggered when a new plugin has been found
void pluginMg_PluginEvent(object sender, PluginEventArgs e)
{
MessageBox.Show(e.Text);
}
}

C# generic event handling - chaining/proxy

I'm trying to come up with a generic way to intercept C# events for processing (e.g. logging, filtering, distributing asynchronously etc.), before passing the event back on to its original consumer.
So to give an example, in the normal flow of things you'd have
class EventProducer
{
public event EventHandler<int> OnEvent;
internal void FireEvent()
{
if (OnEvent != null)
OnEvent(this, 1);
}
}
class EventConsumer
{
public EventConsumer(EventProducer producer)
{
producer.OnEvent += eventHandler;
}
public void eventHandler(object sender, int e)
{
Debug.WriteLine("Consumed " + e);
}
}
In my case, I'd want to insert an additional step between the two.. something like
//event consumer
class EventConsumer
{
//use this to handle one particular event (EventProducer.OnEvent)
InterceptEventHandler<int> EventHandler;
public EventConsumer(EventProducer producer)
{
//just one line of code to insert an event handler to do what I need
//but whoops - cant refer to OnEvent outside of the producer
EventHandler = new InterceptEventHandler<int>(producer.OnEvent, eventHandler);
}
public void eventHandler(object sender, int e)
{
Debug.WriteLine("Consumed " + e);
}
}
//intercepts events, does something with them, then forwards them to original consumer
class InterceptEventHandler<T>
{
public EventHandler<T> Callback;
public InterceptEventHandler(EventHandler<T> eventHook, EventHandler<T> callback)
{
//save callback
Callback = callback;
//subscribe ourselves to the specified event
eventHook += interceptHandler;
}
public void interceptHandler(object sender, T e)
{
//do something with the event here
Debug.WriteLine("Intercepted " + e.ToString());
//then pass callback back to original consumer
Callback(sender, e);
}
}
The problem (as commented above) is that the code doesn't compile because the events themselves are not accessible from outside the producer class. I understand this is because the event handling is implemented as a private class, so can't see any obvious way around this.
Is there any way to intercept and chain events that would allow me to keep the standard event syntax in producer classes?
I realise that I can abandon events, and just use my own equivalent producer/consumer code, but i'll lose the normal benefits of events (e.g. more readable/maintainable, proper syntax highlighting/autocomplete etc.)
EDIT - I also tried fiddling around with the MulticastDelegate class, but while I got the code to compile, I couldn't get the events to be subscribed (effectively OnEvent was always null).
Rather than accepting an EventHandler which is of course not the type of an event, accept an Action<EventHandler> which represents an action that subscribes to the event:
public class InterceptEventHandler
{
public static void Attach<T>(Action<EventHandler<T>> eventHook,
EventHandler<T> callback)
{
eventHook((sender, args) =>
{
doStuffOnFire(sender, args);
callback(sender, args);
});
}
private static void doStuffOnFire<T>(object sender, T e)
{
//...
}
}
You would then call it like so:
public EventConsumer(EventProducer producer)
{
InterceptEventHandler.Attach<int>(
handler => producer.OnEvent += handler,
eventHandler);
}

How can I make consuming custom events on my classes optional?

When I comment out the fm.OnLoaded line below, it gives me an error that OnLoaded is null.
How can I make it optional for the caller of my class to consume the event or not as with .NET classes / events?
using System;
using System.Windows;
namespace TestEventLoaded8282
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
FileManager fm = new FileManager();
//fm.OnLoaded += new FileManager.LoadedHandler(fm_OnLoaded);
fm.Load();
}
void fm_OnLoaded(object obj, FileManagerArgs args)
{
Console.WriteLine("the file manager is loaded: " + args.Message);
}
}
public class FileManager
{
public string Name { get; set; }
public delegate void LoadedHandler(object obj, FileManagerArgs args);
public event LoadedHandler OnLoaded;
public FileManager()
{}
public void Load()
{
Name = "this is the test file manager";
OnLoaded(this, new FileManagerArgs("no errors"));
}
}
public class FileManagerArgs : EventArgs
{
public string Message { get; set; }
public FileManagerArgs(string message)
{
Message = message;
}
}
}
if (OnLoaded != null) {
OnLoaded(this, new FileManagerArgs("no errors"));
}
Check for null before invoking the delegate. The following is a common pattern:
public event EventHandler<FileManagerEventArgs> Loaded;
public void Load()
{
...
OnLoaded(new FileManagerEventArgs("no errors"));
}
protected virtual void OnLoaded(FileManagerEventArgs e)
{
EventHandler<FileManagerEventArgs> handler = this.Loaded;
if (handler != null)
{
handler(this, e);
}
}
You need to check that the OnLoaded event handler is not null before invoking it:
LoadedHandler handler = OnLoaded;
if (handler != null)
{
handler(this, new FileManagerArgs("no errors"));
}
You will need to do this every time you invoke an event handler. The local handler variable above is to catch the case where you can check that the handler is non-null, but something removes the handler before you call it. Creating a local variable captures the handler to prevent this.
An alternative approach is to define the event handler as:
public event LoadedHandler OnLoaded = delegate{};
This declares a default empty event handler, making the null check redundant (there is a slight performance loss using this approach though).

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