Just going through understanding events and delegates and I have a question regarding outputting a different string using the same event and delegate and if it possible. Take this example.
class Program
{
static void Main(string[] args)
{
Publisher b = new Publisher();
Subscriber a = new Subscriber(b);
b.OnLog("12");
}
}
class Publisher
{
public delegate void LogHandler(string message);
public event LogHandler Log;
public void OnLog(string message)
{
//Fire the event - notifying all subscribers
if (Log != null)
Log(message);
}
}
class Subscriber
{
public Subscriber(Publisher pub)
{
//Where B is used - subscribe to it's public event
pub.Log += new Publisher.LogHandler(HandleSomethingHappening);
pub.Log += new Publisher.LogHandler(HandleSomethingHappeningStop);
}
public void HandleSomethingHappening(string message)
{
Console.WriteLine(message);
}
public void HandleSomethingHappeningStop(string message)
{
Console.WriteLine(message);
Console.ReadLine();
}
}
Here, both methods
HandleSomethingHappening and HandleSomethingHappeningStop will output "12". If I wanted handleSomethingHappeningStop to output, lets say "24", do I need to create a new event associated with the same delegate to do this?
so for example
pub.LogOutput24 += new Publisher.LogHandler(HandleSomethingHappeningStop);
Thanks
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 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.
Apologies had a typo...have edited...
I have a weird issue I am not sure about.
In one piece of code I have a class which is called as a singleton which has an event other classes can listen to, pretty straightforward by doing something like
Client.Instance.MyEvent += new EventHandler<EventArgs>(myHandler);
So if I have a generic class:
Class MyTest {
public MyTest() {
System.Console.WriteLine("In Constructor Registering Events");
Client.Instance.MyEvent += new EventHandler<EventArgs>(myHandler);
}
private void myHandler(object sender, EventArgs arg) {
System.Console.WriteLine("Got event!");
}
}
Now if i create the class like:
MyTest mC = new MyTest ();
Client.Instance.FireEvent();
I get the expected "In Constructor Registering Events" and "Got Event"
However if i create the class through Reflection, I do not.
Type mType = typeof(MyTest);
object mT = Activator.CreateInstance(mType);
Client.Instance.FireEvent();
All i get is "In Constructor Registering Events" but i DO NOT get the event fired message. whats going on here? Am i doing something incorrectly in my reflection calls?
Thanks -
I've just tested your claim and, with the proper type, it works the same whether the object is created using new or via reflection.
The following Working Demo can be tested here
public class Program
{
public static void Main(string[] args)
{
Client.Instance.MyEvent += delegate { Console.WriteLine("MY EVENT handled from Main"); };
MyTest mt = new MyTest();
Type mType = typeof(MyTest);
object reflectedMT = Activator.CreateInstance(mType);
Client.Instance.FireEvent();
}
}
public class Client {
private Client() {}
private static Client _inst = new Client();
public static Client Instance { get { return _inst; } }
public void FireEvent() { if(MyEvent != null) MyEvent(this, EventArgs.Empty); }
public event EventHandler<EventArgs> MyEvent;
}
public class MyTest {
public MyTest() {
System.Console.WriteLine("In Constructor Registering Events");
Client.Instance.MyEvent += new EventHandler<EventArgs>(myHandler);
}
private void myHandler(object sender, EventArgs arg) {
System.Console.WriteLine("Got event!");
}
}
I know Events are always associated with Delegates. But, I am missing some core use of Events, and trying to understand that.
I created a simple Event program, as below, and it works perfectly fine.
namespace CompleteRef3._0
{
delegate void someEventDelegate();
class EventTester
{
public event someEventDelegate someEvent;
public void doEvent()
{
if (someEvent != null) someEvent();
}
}
class Program
{
static void EventHandler1()
{
Console.WriteLine("Event handler 1 called..");
}
static void EventHandler2()
{
Console.WriteLine("Event handler 2 called..");
}
static void EventHandler3()
{
Console.WriteLine("Event handler 3 called..");
}
static void Main(string[] args)
{
EventTester evt = new EventTester();
evt.someEvent += EventHandler1;
evt.someEvent += EventHandler2;
evt.someEvent += EventHandler3;
evt.doEvent();
Console.ReadKey();
}
}
}
I replaced the event declaration with delegates. That is I replaced the line public event someEventDelegate someEvent; with someEventDelegate someEvent; on the above program, and I still get the same result. Now, I was confused why we need to use Events, if it can be achieved by Delegates only. What is the real use of Events?
The modified program without events is as below -
namespace CompleteRef3._0
{
delegate void someEventDelegate();
class EventTester
{
someEventDelegate someEvent;
public void doEvent()
{
if (someEvent != null) someEvent();
}
}
class Program
{
static void EventHandler1()
{
Console.WriteLine("Event handler 1 called..");
}
static void EventHandler2()
{
Console.WriteLine("Event handler 2 called..");
}
static void EventHandler3()
{
Console.WriteLine("Event handler 3 called..");
}
static void Main(string[] args)
{
EventTester evt = new EventTester();
evt.someEvent += EventHandler1;
evt.someEvent += EventHandler2;
evt.someEvent += EventHandler3;
evt.doEvent();
Console.ReadKey();
}
}
}
Imagine you have 3 subscribers who are interested in your someEvent. Let's further imagine they are interested in receiving events from the same EventTester instance. For brevity, let's leave out the details of how the exact same instance is passed to all the clients. When I say clients, I mean any class who is a subscriber to the event.
Here is the instance:
EventTester evt = new EventTester();
They have subscribed to the event of the above instance as shown below:
Client 1
evt.someEvent += Client1Handler1;
evt.someEvent += Client1Handler2;
Client 2
evt.someEvent += Client2Handler1;
Client 3
evt.someEvent += Client3Handler1;
evt.someEvent += Client3Handler2;
Client 4:
Imagine client 4 did one of the 3 below:
// 1: See Note 1 below
evt.someEvent = null;
// 2: See Note 2 below
evt.someEvent = new someEventDelegate(MyHandler);
// 3: See Note 3 below
evt.someEvent();
//...
private void MyHandler()
{
MessageBox.Show("Client 4");
}
Note 1
Client 1, 2, and 3 will not be getting any events anymore. Why? Because Client 4 just did this evt.someEvent = null; and in EventTester you have this line of code:
if (someEvent != null) someEvent();
Since that condition will not pass anymore, no event will be raised. There is nothing wrong with the above line of code by the way. But there is the problem with using delegates: It can be assigned to.
Note 2
It has been completely over-written to a brand new instance. Now regardless of the client, they will all see a message box that says "Client 4".
Note 3
Ooops all of a sudden one of the clients is broadcasting the event.
Imagine for a second EventTester was a Button and someEvent was Click. Imagine you had multiple clients interested in the click event of this button. All of a sudden, one client decides no-one else should get notifications (Note 1). Or one client decides that when the button is clicked, it will be handled only 1 way (Note 2). Or it has made the decision that it will decide when a button is clicked even though the button may not have been clicked (Note 3).
If you have an event and one of the clients tried the above, they will not be allowed and get a compile error, like this:
Sure, you can use delegates because behind the scenes an event is a construct that wraps a delegate.
But the rationale of using events instead of delegates is the the same as for using properties instead of fields - data encapsulation. It's bad practice to expose fields (whatever they are - primitive fields or delegates) directly.
By the way, you missed a public keyword before your delegate field to make it possible in the second snippet.
Another "by the way" with the second snippet: for delegates you should use Delegate.Combine instead of "+=".
The main purpose of events is to prevent subscribers from interfering with each other. If you do not use events, you can:
Replace other subscribers by reassigning delegate(instead of using the += operator),
Clear all subscribers (by setting delegate to null),
Broadcast to other subscribers by invoking the delegate.
Source: C# in a Nutshell
public class Program
{
public static void Main()
{
Number myNumber = new Number(100000);
myNumber.PrintMoney();
myNumber.PrintNumber();
Console.ReadKey();
}
}
public class Number
{
private PrintHelper _printHelper;
public Number(int val)
{
_value = val;
_printHelper = new PrintHelper();
//subscribe to beforePrintEvent event
_printHelper.beforePrintEvent += printHelper_beforePrintEvent;
}
//beforePrintevent handler
void printHelper_beforePrintEvent(string message)
{
Console.WriteLine("BeforePrintEvent fires from {0}", message);
}
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
public void PrintMoney()
{
_printHelper.PrintMoney(_value);
}
public void PrintNumber()
{
_printHelper.PrintNumber(_value);
}
}
public class PrintHelper
{
public delegate void BeforePrintDelegate(string message);
public event BeforePrintDelegate beforePrintEvent;
public PrintHelper()
{
}
public void PrintNumber(int num)
{
if (beforePrintEvent != null)
beforePrintEvent.Invoke("PrintNumber");
Console.WriteLine("Number: {0,-12:N0}", num);
}
public void PrintDecimal(int dec)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintDecimal");
Console.WriteLine("Decimal: {0:G}", dec);
}
public void PrintMoney(int money)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintMoney");
Console.WriteLine("Money: {0:C}", money);
}
public void PrintTemperature(int num)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintTemerature");
Console.WriteLine("Temperature: {0,4:N1} F", num);
}
public void PrintHexadecimal(int dec)
{
if (beforePrintEvent != null)
beforePrintEvent("PrintHexadecimal");
Console.WriteLine("Hexadecimal: {0:X}", dec);
}
}
This has turned out to be quite a lengthy question, so thank you in advance to all those who give up their time to read it and comment/answer :)
Edits
This Question has been majorly simplified.
Example code is now a complete, simple program
I am using an observer pattern implemented through interfaces:
public interface IObserver<in T>where T:EventArgs
{
void Update(object sender, T e);
}
public interface ISubject<in T, TU>where TU:EventArgs
{
event EventHandler<TU> Notify;
T State { set; }
void Attach(Action<object,TU> callback);
void Detach(Action<object, TU> callback);
}
I have created two simple classes which implement these interfaces
The MyObserver object will simply output a string to the console window when a Notify event is raised in the MySubject object.
public class MyObserver:IObserver<TestEventArgs>
{
private ISubject<bool, TestEventArgs> _subject;
public MyObserver(ISubject<bool, TestEventArgs> subject)
{
_subject = subject;
}
public void Subscribe()
{
_subject.Attach(Update);
}
public void Unsubscribe()
{
_subject.Detach(Update);
}
public void Update(object sender, TestEventArgs e)
{
Console.WriteLine(e.TestMessage);
}
}
public class MySubject:ISubject<bool, TestEventArgs>
{
public void ObservableEvent(string message)
{
InvokeNotify(message);
}
private void InvokeNotify(string message)
{
EventHandler<TestEventArgs> handler = Notify;
if(handler != null)
{
handler(this, new TestEventArgs(message));
}
}
public event EventHandler<TestEventArgs> Notify;
public bool State
{
set { throw new NotImplementedException(); }
}
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
public void Detach(Action<object, TestEventArgs> callback)
{
Notify -= new EventHandler<TestEventArgs>(callback);
}
}
public class TestEventArgs:EventArgs
{
public TestEventArgs(string message)
{
TestMessage = message;
}
public string TestMessage { get; private set; }
}
This test program shows that:
before myObserver has subscribed to the event no message is output to the Console window.
after myObserver has subscribed to the Notify event the message is output to the Console window.
after myObserver has UNsubscribed from the Notify event the message is still output to the Console window
static void Main(string[] args)
{
MySubject mySubject = new MySubject();
MyObserver myObserver = new MyObserver(mySubject);
//we have not subscribed to the event so this should not be output to the console
mySubject.ObservableEvent("First Test");
myObserver.Subscribe();
//we are now subscribing to the event. This should be displayed on the console window
mySubject.ObservableEvent("Second Test");
myObserver.Unsubscribe();
//We have unsubscribed from the event. I would not expect this to be displayed
//...but it is!
mySubject.ObservableEvent("Third Test");
Console.ReadLine();
}
The issue I'm having is that the unsubscribe process is not working.
I really don't understand why.
Questions
Why is the unsubscribe process not working?
What happens when comparing 2 event handlers? How are they defined as equal or not? This may lead to an answer to why the invocation list Contains method always returns false.
I suspect your problem is that this code:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
Actually allocates a new object, as does the corresponding Detach code. So what's being detached isn't the same thing as what's being attached.
I'm not sure, but you might be able to fix it by changing your Attach and Detach so that they're:
void Attach(EventHandler<TU> callback);
void Detach(EventHandler<TU> callback);
And in the client code:
public void Attach(EventHandler<TestEventArgs> callback)
{
Notify += callback;
}
public void Detach(EventHandler<TestEventArgs> callback)
{
Notify -= callback;
}
I haven't actually tried to compile this, but it looks like it should work.
Or, if the compiler can do the type conversion:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += callback;
}
Might be worth a shot.