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).
Related
I want to be able to have an object add one of its methods to an EventHandler that is passed to it and give said method the ability to remove itself from the EventHandler.
public class EventRaiser {
public event EventHandler event1
public event EventHandler event2
public void fire() {
event1?.Invoke(this, null);
event2?.Invoke(this, null);
}
}
public class EventSubscriber {
EventHandler eh;
public EventSubscriber(EventHandler eh) {
this.eh = eh;
eh += receive;
}
public void receive(object obj, EventArgs data) {
// Do stuff.
if(condition) eh -= receive;
}
}
public class MainClass {
public void Main() {
EventRaiser er = new EventRaiser();
EventSubscriber es1 = new EventSubscriber(er.event1);
EventSubscriber es2 = new EventSubscriber(er.event2);
er.fire();
}
}
The above code does not compile as I cannot even pass er.event1 or er.event2 to EventSubscriber ("The event can only appear in the left hand side of +=..."). Removing the event keyword from the EventHandlers fixes this issue but unsubscribing does not work properly. Is there a way to make this work? Use pointers maybe?
The problem here comes from you passing an EventHandler, not the list holding the delegates behind it itsself. Basically the "list of method pointers" to your handlers.
As you can see, in the declaration of event1 you have the keyword event, which is missing when you pass it somewhere else.
Unfortunately you cannot extract the "delegate holder" of an event easily.
Basically at the time you want to register your handler to an event you somehow need a compile time reference to it, in order to be able to += and -= to it.
You could do the following:
public class EventRaiser
{
public delegate void Event1(string args);
public List<Event1> handlers = new List<Event1>();
public void register(Event1 handler)
{
handlers.Add(handler);
}
public void unregister(Event1 handler)
{
handlers.Remove(handler);
}
public void fire()
{
handlers.ForEach(handler => handler("myEventArgs"));
}
}
public class EventSubscriber
{
Action<Event1> registerAction;
Action<Event1> unregisterAction;
public EventSubscriber(Action<Event1> register, Action<Event1> unregister)
{
registerAction = register;
unregisterAction = unregister;
registerAction(receive);
}
public void receive(string args)
{
// Do stuff.
unregisterAction(receive);
}
}
public class MainClass
{
public void Main()
{
EventRaiser er = new EventRaiser();
EventSubscriber es1 = new EventSubscriber(er.register, er.unregister);
er.fire();
}
}
I have to access data from an event in another class.
In that class things are like this:
namespace MavLink
{
public class Mavlink
{
...
public event PacketReceivedEventHandler PacketReceived;
...
private void ProcessPacketBytes(byte[] packetBytes, byte rxPacketSequence)
{
...
if (PacketReceived != null)
{
PacketReceived(this, packet);
}
...
}
}
public delegate void PacketReceivedEventHandler(object sender, MavlinkPacket e);
}
And in the main I've tried to do like this:
...
m.ParseBytes(newlyReceived);
m.PacketReceived += (sender, e) => {
Console.WriteLine(e.SystemId);
Console.WriteLine(e.ComponentId);
Console.WriteLine(e.SequenceNumber);
Console.WriteLine(e.TimeStamp);
Console.WriteLine(e.Message);
};
But it doesn't seem work.
Thank you for your help.
Edit:
It compiles without errore but nothing is printed on the console. I don't know how to check if the event has been rised though.
Well on this what I read I have created a usual Event that will give you some data to access.
We start with creating the event.
public delegate void PacketReceivedEventHandler(var pPacket);
public event PacketReceivedEventHandler PacketReceived;
I put a var in there cause I didn't exactly saw what you are "delivering". Just change it into whatever you need.
So, lets continue. Place this when the Event needs to be triggered.
if (Mavlink.PacketReceived != null)
Mavlink.PacketReceived(YourPackage);
YourPackage is whatever you want to deliver.
But you need to subscribe a event to do stuff with it.
Mavlink.PacketReceived += Mavlink_PacketReceived;
C# usually created a class if you double tab after the +=. But here is the class I created.
private void Mavlink_PacketReceived(var pPacket)
{
if(pPacket != null)
{
Console.WriteLine(pPacket.SystemId);
Console.WriteLine(pPacket.ComponentId);
Console.WriteLine(pPacket.SequenceNumber);
Console.WriteLine(pPacket.TimeStamp);
Console.WriteLine(pPacket.Message);
}
}
I dont know what comes after that in your code, but make sure that there will be something to make you command line wait so it wont close after firing that.
I made an example, which works fine, hope it helps. Replace EventHandler by your PacketRecievedEventHandler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var sender = new Sender();
var reciever = new Reciever(sender);
sender.ProcessPacketBytes(null, byte.MaxValue);
Console.ReadLine();
}
}
/// <summary></summary>
public class Sender
{
private readonly object _objectLock = new object();
public event EventHandler PacketReceived
{
add
{
lock (_objectLock)
{
PacketRecievedEvent += value;
}
}
remove
{
lock (_objectLock)
{
PacketRecievedEvent -= value;
}
}
}
private event EventHandler PacketRecievedEvent;
public void ProcessPacketBytes(byte[] packetBytes, byte rxPacketSequence)
{
EventHandler handler = this.PacketRecievedEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
public class Reciever
{
public Reciever(Sender sendertest)
{
sendertest.PacketReceived += (sender, e) =>
{ Console.WriteLine(e.GetType()); };
}
}
}
I have 3 classes namely Login, Barcode, and the Main.
Login class just contains the authentication of the users.
Barcode class has the following snippet code:
class Barcode
{
public delegate void BarcodeReadHandler(object sender, BarcodeEventArgs e);
public event BarcodeReadHandler BarcodeReadOut;
public Barcode()
{
//.. some codes for getting data on the scanner
BarcodeEventArgs args = new BarcodeEventArgs(scannedData);
BarcodeReadOut(this, args);
}
}
While in Main class, the subsciption of the Barcode event is done:
public partial class Main : Form
{
private Barcode barcode = null;
public Main()
{
barcode.BarcodeReadOut += new barcode.BarcodeReadHandler(getBarcodeStr);
}
//This is called before log-out.
public void removeInstance()
{
barcode.BarcodeReadOut -= new barcode.BarcodeReadHandler(getBarcodeStr);
}
private void getBarcodeStr(object sender, BarcodeEventArgs e)
{
//some code
}
}
The duplication of event subscription happens when I try to logout and login again.
When I tried to debug, BarcodeReadOut is called twice.
In logout, the removeInstance() is called and the Main form is Close() and Dispose() before opening the login screen.
Can someone help me on how can I avoid the duplication of the said events?
I also have done this before registering the event but nothing happens:
public Main()
{
barcode.BarcodeReadOut -= new barcode.BarcodeReadHandler(getBarcodeStr);
barcode.BarcodeReadOut += new barcode.BarcodeReadHandler(getBarcodeStr);
}
You should add and remove the handler as follows:
public partial class Main : Form
{
private Barcode barcode = null;
public Main()
{
barcode.BarcodeReadOut += getBarcodeStr;
}
//This is called before log-out.
public void removeInstance()
{
barcode.BarcodeReadOut -= getBarcodeStr;
}
private void getBarcodeStr(object sender, BarcodeEventArgs e)
{
//some code
}
}
Also: You don't need to define a custom delegate, you can use the generic EventHandler:
public event EventHandler<BarcodeEventArgs> BarcodeReadOut;
It would be good to move all your logic that works with Barcode to a separate class. And it might be good to add a custom event that notifies other classes (a Form class in your case) that event has occurred :
class Barcode
{
public delegate void BarcodeReadHandler(object sender, BarcodeEventArgs e);
public event BarcodeReadHandler BarcodeReadOut;
public Barcode()
{
//.. some codes for getting data on the scanner
BarcodeEventArgs args = new BarcodeEventArgs(scannedData);
BarcodeReadOut(this, args);
}
}
class BarcodeWorker
{
private Barcode barcode = null;
private BarcodeReadHandler handler;
public event BarcodeEventArgs scanComplete;
BarcodeWorker(Barcode barcode)
{
if(barcode == null) this.barcode = barcode;
}
public AddEventHandler()
{
if(handler != null) return;
handler = new BarcodeReadHandler(getBarcodeStr);
barcode.BarcodeReadOut += handler;
}
//This is called before log-out.
public void RemoveEventHandler()
{
barcode.BarcodeReadOut -= handler;
handler = null;
}
private void getBarcodeStr(object sender, BarcodeEventArgs e)
{
scanComplete(sender, e);
}
}
And use it like this:
BarcodeWorker barcode = new BarcodeWorker();
barcode.scanComplete += // your delegate with event handler or with anonymous method here;
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);
}
}
I have tried to raise event in cSharp to notify code change in my application in order to have tellDontAsk scenario.
I have simple class that implement from event class
public class SimpleTellDontAsk : ISomeEvent
{
public void doSomething(string text, EventHandlerArgs args)
{
Console.WriteLine("Email this message {0}", text);
//sending message with email
args.IsDo = true;
RaiseEvent(this, args);
}
public event EventHandler RaiseEvent;
}
I define my event class like below
public interface ISomeEvent
{
event EventHandler RaiseEvent;
}
public class SomeEvent : ISomeEvent
{
public event EventHandler RaiseEvent;
}
public class EventHandlerArgs : EventArgs
{
public bool IsDo { get; set; }
}
I try to use my code with Nunit test
[TestFixture]
public class TestSimpleTellDontAsk
{
[Test]
public void Given_Text_When_doSomething_Then_ShouldPubliushArgs()
{
var tellDontAsk = new SimpleTellDontAsk();
var senderEventHandlerArgs = new EventHandlerArgs();
tellDontAsk.doSomething("test message", senderEventHandlerArgs);
Assert.IsTrue(senderEventHandlerArgs.IsDo);
}
}
When I run the test it comes up with null reference exception
System.NullReferenceException : Object reference not set to an instance of an object.
I believe something is missing but could not figure out , any idea ?
RaiseEvent isn't attached
bool eventFired = false;
var tellDontAsk = new SimpleTellDontAsk();
tellDontAsk.RaiseEvent += (o, e) =>
{
if (e.IsDo)
eventFired = true;
};
tellDontAsk.doSomething("test message");
Assert.IsTrue(eventFired);
Also if you want to use your own EventArgs "EventHandlerArgs" you should go for the Generic EventHandler.
The eventargs shouldnt be provided in the parameters to the method, they should be created when we need to raise the event.
public class SimpleTellDontAsk : ISomeEvent
{
public void doSomething(string text)
{
Console.WriteLine("Email this message {0}", text);
//sending message with email
if (RaiseEvent != null) //Check if we have listeners
{
EventHandlerArgs args = new EventHandlerArgs();
args.IsDo = true;
RaiseEvent(this, args);
}
}
public event EventHandler<EventHandlerArgs> RaiseEvent;
}
RaiseEvent does not have handler attached , modified the code for attaching default handler , i am still not clear how it is implementing tell don't ask principle , you are just checking the IsDo property , what is the use of ISomeEvent?
public interface ISomeEvent
{
event Action<ISomeEvent, EventHandlerArgs> RaiseEvent;
}
public class SomeEvent : ISomeEvent
{
public event Action<ISomeEvent, EventHandlerArgs> RaiseEvent;
}
public class EventHandlerArgs : EventArgs
{
public bool IsDo { get; set; }
}
public class SimpleTellDontAsk : ISomeEvent
{
public SimpleTellDontAsk()
{
RaiseEvent = (s,e) => { };
}
public void doSomething(string text, EventHandlerArgs args)
{
Console.WriteLine("Email this message {0}", text);
//sending message with email
args.IsDo = true;
RaiseEvent(this, args);
}
public event Action<ISomeEvent, EventHandlerArgs> RaiseEvent;
}