I noticed most of the developers are using event for callback and I'm not sure whether I'm doing in a right way.
I noticed most of developers' codes look like this:
public delegate void SampleDelegate();
public static event SampleDelegate SampleEvent;
While the way I do "event" look like this:
public delegate void SampleDelegate();
public SampleDelegate SampleEvent; // notice that I didn't set to static and not an event type
I hope someone could explain to me what's the differences between both codes? Which way of doing delegate is a better practice? Is it better to set it as a static?
Let's look at your code:
public delegate void SampleDelegate();
public SampleDelegate SampleEvent;
It's not an event. Because you can call SampleEvent outside the containing class:
public class TestClass
{
public delegate void SampleDelegate();
public SampleDelegate SampleEvent;
}
public void TestMethod()
{
var a = new TestClass();
a.SampleEvent();
}
Also, you can set it to new value:
public void TestMethod()
{
var a = new TestClass();
a.SampleEvent = null;
}
And this is not the event behavior.
An interface can not contain this "event":
public interface ITestInterface
{
//TestClass.SampleDelegate SampleEvent; //does not compile
}
So the right way - to add event word for real events:
public class TestClass : ITestInterface
{
public delegate void SampleDelegate();
public event SampleDelegate SampleEvent;
private void FireEvent()
{
var handler = SampleEvent;
if (handler != null)
handler();
}
}
public interface ITestInterface
{
event TestClass.SampleDelegate SampleEvent;
}
And now you can only call it from the containing class:
public void TestMethod()
{
var a = new TestClass();
//a.SampleEvent(); //does not compile
a.SampleEvent += A_SampleEvent; //subscribe to event
}
private void A_SampleEvent()
{
Console.Write("Fired"); //fired when FireEvent method called
}
So, you must uderstand difference between delegates and events. And choose the appropriate way for different situations:
Events - when you need to nodify other classes (one or more) about some changes.
Delegetes - when you just want to declare a method signature and pass the implementation from outside (simplified explanation).
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();
}
}
Let's say I have an event, declared as so:
public class OnGooseHonkEvent{
public delegate void OnGooseHonk();
public static event OnGooseHonk OnGooseHonkEvent;
}
And then in an assembly compiled to a dll, I have the following code:
namespace ExampleMod
{
public class Class1
{
public Class1()
{
}
public void OnGooseHonk()
{
UntitledGooseAPI.Log("Goose Honked");
}
}
}
How would I be able to go about subscribing ExampleMod.Class1.OnGooseHonk to OnGooseHonkEvent.OnGooseHonkEvent? And it has to be done with the Assembly module, as I am loading all assemblies from a directory programatically.
I might be a little confused the way the OnGooseHonkEvent class is setup. Changing the delegate to a method that can be called on the object, and setting up the OnGooseHonkedEvent as a Eventhandler<EventArgs> instead of an event, I got this:
public class OnGooseHonkEvent
{
// Calling this will notify every event subscriber the goose has honked
public void HonkTheGoose()
{
OnGooseHonkedEvent?.Invoke(this, EventArgs.Empty);
}
public static event EventHandler<EventArgs> OnGooseHonkedEvent;
}
public class Class1
{
public Class1()
{
OnGooseHonkEvent.OnGooseHonkedEvent += OnGooseHonk;
}
public void OnGooseHonk(object sender, EventArgs e)
{
UntitledGooseAPI.Log("When any instance of OnGooseHonkEvent calls the HonkTheGoose() method, this should get fired");
}
}
I started to transform my push -> pull bridge to a much simpler construct with Reactive Extensions.
So now I have a class with a (private) event, and an Observable created from it.
class WithEvents {
public class MyEvent {}
private delegate void MyEventHandler(MyEvent e);
private event MyEventHandler EventRaised;
Public IObservable<MyEvent> TheEvents;
public void Foo() {
EventRaised(new MyEvent());
}
}
Thing is, this event seems like unneeded scaffolding here. So I was wondering: is there a way to construct a 'bare' Observable, that I can just 'push' events to?
class WithChannel {
public class MyEvent {}
public IObservable<MyEvent> EventRaised {get} = new Channel<MyEvent>();
public void Foo() {
((Channel)EventRaised).DoNext(new MyEvent());
}
}
Yes, there is a thing called Subject (in System.Reactive.Subjects namespace) which does exactly that:
class WithChannel {
public class MyEvent {
}
private readonly Subject<MyEvent> _event;
public WithChannel() {
_event = new Subject<MyEvent>();
}
public IObservable<MyEvent> EventRaised => _event;
public void Foo() {
_event.OnNext(new MyEvent());
}
}
Usage of subjects is generally not recommended, but for this specific task I think it's fine.
I want an abstract class that raises an event, this event will be raised by the concrete class.
What I want is when I use another class to listen to these events the signature of the delegate should have the concrete type not the abstract, I don't want to cast it.
For the moment I have come up with this solution. It works but I don't find it particularly clever especially because of the "STUPID, DOESN'T MAKE SENSE......" part.
Here is my solution :
public delegate void ClassAEventHandler<TClassA>(TClassA classA) where TClassA : ClassA;
//Abstract class that raise Event
public abstract class ClassA<TClassA> : where TClassA : ClassA
{
public event ClassAEventHandler<TClassA> onClassEventRaised;
private TClassA eventClassA;
public void registerEventClass(TClassA classA)
{
this.eventClassA = classA;
}
public void raiseClassEvent()
{
this.onClassEventRaised(this.eventClassA);
}
}
// Exemple of concrete type
public class ClassB : ClassA<ClassB> // <------ IT SEEMS DUMB
{
public void action()
{
//Do something then raise event
this.raiseClassEvent();
}
public void saySomething() {};
}
// Exemple of concrete type
public class ClassC : ClassA<ClassC> // <------ IT SEEMS DUMB
{
public void command()
{
//Do something then raise event
this.raiseClassEvent();
}
public void destroySomething() {};
}
//Class that listen to the event raised
public class MyEventListener
{
private ClassB classB;
private ClassC classC;
public MyEventListener()
{
this.classB = new ClassB();
this.classB.registerEventClass(this.classB); // <------ STUPID, DOESN'T MAKE SENSE......
this.classB.onClassEventRaised += classB_onClassEventRaised;
this.classC = new ClassC();
this.classC.registerEventClass(this.classC); // <------ STUPID, DOESN'T MAKE SENSE......
this.classC.onClassEventRaised += classC_onClassEventRaised;
}
public void classB_onClassEventRaised(ClassB classB)
{
classB.saySomething();
}
public void classC_onClassEventRaised(ClassC classC)
{
classC.destroySomething();
}
//What i don't want
/*
public void classB_onClassEventRaised(ClassA classA)
{
((classB)classA).saySomething();
}
*/
}
First of all, you're not following regular event design in .NET.
Instead of implementing your own delegate, use EventHandler<TArgs>, and create a derived class of EventArgs.
Your CustomEventArgs should have a T generic parameter:
public class CustomEventArgs<T> where T : A
{
private readonly T _instance;
public CustomEventArgs(T instance)
{
_instance = instance;
}
public T Instance { get { return _instance; } }
}
Also, don't implement a custom way of registering events. If you want to encapsulate how handlers are added to the event, you need to use event accessors.
Finally, you could implement your classes as follows:
public class A<T> where T : A
{
private event EventHandler<CustomEventArgs<T>> _someEvent;
// An event accessor acts like the event but it can't be used
// to raise the event itself. It's just an accessor like an special
// event-oriented property (get/set)
public event EventHandler<CustomEventArgs<T>> SomeEvent
{
add { _someEvent += value; }
remove { _someEvent -= value; }
}
protected virtual void RaiseSomeEvent(CustomEventArgs<T> args)
{
// If C# >= 6
_someEvent?.Invoke(this, args);
// Or in C# < 6
// if(_someEvent != null) _someEvent(this, args);
}
}
public class B : A<B>
{
public void DoStuff()
{
// It's just about raising the event accessing the whole
// protected method and give an instance of CustomEventArgs<B>
// passing current instance (i.e. this) to CustomEventArgs<T>
// constructor.
RaiseSomeEvent(new CustomEventArgs<B>(this));
}
}
Now, if you try to handle SomeEvent, you'll get the CustomEventArgs<B> typed as B instead of A:
B b = new B();
b.SomeEvent += (sender, args) =>
{
// args.Instance is B
B instance = args.Instance;
};
b.DoStuff(); // Raises SomeEvent internally
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();