I have a publisher class and a subscriber class. The publisher class has an event and the subscriber wants to execute one of its own methods when this event is raised.
Here is my publisher class with its event:
public delegate EventHandler<MyEventArgs> MyEventHandler(object sender, MyEventArgs args);
public class MyEventArgs : EventArgs
{
public string Content { set; get; }
}
public class Publisher
{
public event MyEventHandler MyCustomEvent;
public void TriggerEvent()
{
if(MyCustomEvent!=null)
MyCustomEvent(this, new MyEventArgs{ Content = "Geeee! This isn't working!" });
}
}
And here is the subscriber:
class Subscriber
{
static void Main(string[] args)
{
Publisher publisher = new Publisher();
//hook the event to a method
publisher.MyCustomEvent += (s, e) => delegate
{
PrintThis(e.Content);
};
//Do something to trigger the event
publisher.TriggerEvent();
}
static public void PrintThis(string content)
{
Console.Write(content);
}
}
The publisher should not be aware of the subscriber. I want the subscriber to be able to execute its PrintThis function when the publisher invokes the event. But this is not working. What am I doing wrong here?
You haven't defined the event handler properly.
Do it like this:
public delegate void MyEventHandler(object sender, MyEventArgs args);
Notice that the return type is void.
And then the event handler itself should be:
publisher.MyCustomEvent += (s, e) =>
{
PrintThis(e.Content);
};
Also your TriggerEvent method is safer if defined this way:
public void TriggerEvent()
{
var mce = MyCustomEvent;
if (mce!=null)
{
mce(this, new MyEventArgs{ Content = "Geeee! This isn't working!" });
}
}
In a multi-threaded environment the MyCustomEvent handler could change between the null check and the invocation.
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 want pass event to another event,now I use function to do that.
Can C# pass event like b.WriteEvent += a.WriteEvent ?
If I had a lot class,and just want pass argument to above class.
I want write like : a.event += b.event. b.event += c.event
Instead of a lot no use method.
Thanks.
class Program
{
static void Main(string[] args)
{
ClassA a = new ClassA();
ClassB b = new ClassB();
a.WriteEvent += MainWrite;
b.WriteEvent += a.WireFunction; // Now I use
//b.WriteEvent += a.WriteEvent; <= Can I use like this ?
b.WireFunction("some str");
Console.ReadLine();
}
static void MainWrite(string str)
{
Console.WriteLine(str);
}
}
class ClassA
{
public event Handler WriteEvent;
public void WireFunction(string str)
{
WriteEvent(str);
}
}
class ClassB
{
public event Handler WriteEvent;
public void WireFunction(string str)
{
WriteEvent(str);
}
}
public delegate void Handler(string str);
Fact:You cannot provide an event that subscribes to an event.
All delegates (events, actions or funcs) ar multicast delegates in C#.
That means you can subscribe to an event multiple times.
In order to subscribe to an event you have to provide an action or a function. (I use the term function instead of method because we may provide a lambda)
What follows is snipped that subscribes all subscribers of Event1 to Event2.
I believe this is what you intend to do.
public class SomeClass
{
public event EventHandler Event1;
public event EventHandler Event2;
public SomeClass()
{
Event1 += Subscriber1;
Event1 += Subscriber2;
var subscribers = Event1.GetInvocationList();
if(subscribers != null)
{
foreach(var subscriber in subscribers)
{
EventHandler realSubscriber = (EventHandler)subscriber;
Event2 += realSubscriber;
}
}
Event1(this, EventArgs.Empty);
Event2(this, EventArgs.Empty);
}
public void Subscriber1(object sender, EventArgs e)
{
Console.WriteLine("Subscriber 1 invoked");
}
public void Subscriber2(object sender, EventArgs e)
{
Console.WriteLine("Subscriber 2 invoked");
}
}
Creating an instance of the SomeClass will print:
Subscriber 1 invoked
Subscriber 2 invoked
Subscriber 1 invoked
Subscriber 2 invoked
EDIT:
I tried to move the logic to an extension method and also to a normal utility method. Both did not work very well because events are null when they have no subscribers. Passing an event without subscribers would then result in the same behaviour as if null was passed. For now, this is the best I could come up with.
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 created a very simple dummy program to understand Delegates and events. In my below program I am simple calling a method. When I call a method, five methods are automatically called with the help of delegates and events.
Kindly take a look at my program and do let me know where I am wrong or right as this is my first time using delegates and events.
using System;
namespace ConsoleApplication1
{
public delegate void MyFirstDelegate();
class Test
{
public event MyFirstDelegate myFirstDelegate;
public void Call()
{
Console.WriteLine("Welcome in Delegate world..");
if (myFirstDelegate != null)
{
myFirstDelegate();
}
}
}
class AttachedFunction
{
public void firstAttachMethod()
{
Console.WriteLine("ONE...");
}
public void SecondAttachMethod()
{
Console.WriteLine("TWO...");
}
public void thirdAttachMethod()
{
Console.WriteLine("THREE...");
}
public void fourthAttachMethod()
{
Console.WriteLine("FOUR...");
}
public void fifthAttachMethod()
{
Console.WriteLine("FIVE...");
}
}
class MyMain
{
public static void Main()
{
Test test = new Test();
AttachedFunction attachedFunction = new AttachedFunction();
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.firstAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.SecondAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.thirdAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.fourthAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.fifthAttachMethod);
test.Call();
Console.ReadLine();
}
}
}
Events are implemented using Delegates. That said by convention events take the form of:
void EventHandler(Object sender, EventArgs args);
EventHandler is actually a delegate defined in .Net. EventArgs is a class in .Net that acts as a placeholder to pass additional information. If you have additional information you would create a class that derived from EventArgs and contained properties for the additional data; therefore you would create your own delegate like so:
void MyEventHandler(Object sender, MyEventArgs args);
Microsoft has a tutorial on events here and also describes defining and raising events here
This is a common pattern with dealing with events:
// define the delegate
public delegate void CustomEventHandler(object sender, CustomEventArgs e);
// define the event args
public class CustomEventArgs : EventArgs
{
public int SomeValue { get; set; }
public CustomEventArgs( int someValue )
{
this.SomeValue = someValue;
}
}
// Define the class that is raising events
public class SomeClass
{
// define the event
public event CustomEventHandler CustomEvent;
// method that raises the event - derived classes can override this
protected virtual void OnCustomEvent(CustomEventArgs e)
{
// do some stuff
// ...
// fire the event
if( CustomEvent != null )
CustomEvent(this, e);
}
public void SimulateEvent(int someValue)
{
// raise the event
CustomEventArgs args = new CustomEventArgs(someValue);
OnCustomEvent(args);
}
}
public class Main
{
public static void Main()
{
SomeClass c = new SomeClass();
c.CustomEvent += SomeMethod;
c.SimulateEvent(10); // will cause event
}
public static void SomeMethod(object sender, CustomEventArgs e)
{
Console.WriteLine(e.SomeValue);
}
}
Try putting the line
public delegate void MyFirstDelegate();
inside the Test class.
Also, use the Invoke function on the event instead, i.e.
myFirstDelegate.Invoke();
I would like to create a method that takes an event as an argument and adds eventHandler to it to handle it properly. Like this:
I have two events:
public event EventHandler Click;
public event EventHandler Click2;
Now I would like to pass a particular event to my method like this (pseudocode):
public AttachToHandleEvent(EventHandler MyEvent)
{
MyEvent += Item_Click;
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);
Is it possible?
I've noticed that this code worked fine, and returned to my project and noticed that when I pass an event declared in my class, it works, but when I pass event from other class it still does not work.
What I get is this error:
The event
'System.Windows.Forms.ToolStripItem.Click'
can only appear on the left hand side
of += or -=
My original answer was suitable from within the class that defined the event, but you've since updated your question to reflect that you wish to accomplish this from outside the defining class, so I've stricken that.
Only the class that defines an event can refer to the implicit delegate variable that the event uses. From outside that class, you only have access to the add and remove methods, via += and -=. This means that you can't do what you're asking, directly. You can, however, use a functional approach.
class A{
public event EventHandler Event1;
public void TriggerEvent1(){
if(Event1 != null)
Event1(this, EventArgs.Empty);
}
}
class B{
static void HandleEvent(object o, EventArgs e){
Console.WriteLine("Woo-hoo!");
}
static void AttachToEvent(Action<EventHandler> attach){
attach(HandleEvent);
}
static void Main(){
A a = new A();
AttachToEvent(handler=>a.Event1 += handler);
a.TriggerEvent1();
}
}
I did it like this:
public AttachToHandleEvent(Object obj, string EventName)
{
EventInfo mfi = obj.GetType().GetEvent(EventName);
MethodInfo mobj = mfi.GetAddMethod();
mobj.Invoke(obj, new object[] { Item_Click});
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool "Click");
Thank you all for advice. This solution could not be done without your help.
It's not possible. You can use a delegate instead of an event if that meets your needs.
Just write tool.Click += Item_Click;
Edit: From MSDN "Events can only be invoked from within the class or struct where they (it) are declared". So what you are trying to do is not possible. Could you elaborate more on your needs? Why would you want to pass an event as a parameter?
delegate void doIt(object sender, object data);
event doIt OnDoIt;
void add(doIt theDel)
{
OnDoIt += theDel;
}
void doIt1(object a, object b)
{
}
void doIt2(object a, object b)
{
}
void add()
{
add(doIt1);
add(doIt2);
}
Your question suggests that you got some mechanisms wrong:
You can't pass events!
You most probably want to pass a function as a parameter, so the calling method will call that other method at some point. In technical terms this is a delegate. I suggest using the already defined Action class. Here's an example snippet:
void MyFunction (string otherArguments, Action onFinished){
...
if (onFinished != null)
onFinished.Invoke();
}
The nice thing about this is that when calling MyFunction you can declare the Action using the inline syntax:
MyFunction("my other argument", ()=>{
///do stuff here, which will be execuded when the action is invoked
});
I pass functions/methods (instead of events) like this:
class A
{
public void something()
{
var myAction =
new Action<object, object>((sender, evArgs) => {
MessageBox.Show("hiii, event happens " + (evArgs as as System.Timers.ElapsedEventArgs).SignalTime);
});
B.timer(myAction);
}
}
class B
{
public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
myTimer.Interval = 2000;
myTimer.Start();
}
}
Giving an update to this question with an object oriented solution.
Instead of using an Action<EventHandler> that registers the event, you could create an object handling that for you
public class AEvent
{
private readonly A aInstance;
private AEvent(A instance) {
aInstance = instance;
}
public void Add(EventHandler eventHandler)
=> a.Event1 += eventHandler;
public void Remove(EventHandler eventHandler)
=> a.Event1 -= eventHandler;
public EventHandler Invoke => aInstance.Event1;
}
Then later on use that object like this:
static void Main(){
A a = new A();
AEvent aEvent = new AEvent(A)
aEvent.Add(handler);
a.Invoke();
}
One approach I haven't seen here would be to create an object which has delegates for subscribe and unsubscribe. Here is a complete example program.
class Program
{
private event EventHandler<EventArgs> eventHandler;
public static void Main(string[] args)
{
Program program = new Program();
Thing thing = new Thing(new EventWrapper<EventArgs>(
delegate(EventHandler<EventArgs> handler) { program.eventHandler += handler; },
delegate(EventHandler<EventArgs> handler) { program.eventHandler -= handler; }
));
// events are fired
program.eventHandler?.Invoke(program, EventArgs.Empty);
thing.Unsubscribe();
}
}
class Thing
{
private readonly Action<EventHandler<EventArgs>> _unsubscribeEventHandler;
public Thing(EventWrapper<EventArgs> eventHandler)
{
this._unsubscribeEventHandler = eventHandler.Unsubscribe;
eventHandler.Subscribe?.Invoke(OnEvent);
Console.WriteLine("subscribed");
}
private void OnEvent(object? sender, EventArgs e)
{
Console.WriteLine("event fired");
}
public void Unsubscribe()
{
_unsubscribeEventHandler?.Invoke(OnEvent);
Console.WriteLine("unsubscribed");
}
}
class EventWrapper<T> where T : EventArgs
{
public Action<EventHandler<T>> Subscribe { get; private set; }
public Action<EventHandler<T>> Unsubscribe { get; private set; }
public EventWrapper(Action<EventHandler<T>> subscribe, Action<EventHandler<T>> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
}
In this example, we created a new class called EventWrapper<T> which wraps delegates for += and -= and exposes them with Subscribe and Unsubscribe methods. The delegates will need to be created by the class which created the event.