How can I have a "generic" delegate? - c#

My current situation looks like this:
I have these two delegates in a separate file:
public delegate void EventHandler(Object obj, EventArgs e);
public delegate void OtherEventHandler(Object obj, OtherEventArgs e);
I have an Event class that look like this:
class Event {
EventHandler _handler;
public Event(EventHandler handler) {
_handler = handler;
}
}
I have another class that inherits Event this way:
class OtherEvent : Event {
OtherEventHandler _handler;
public OtherEvent (OtherEventHandler handler) : base(handler) {
_handler = handler;
}
}
This one is where the problem occurs. The error is with the part on base(handler). Because handler in OtherEvent is an OtherEventHandler, the base class cannot accept it; the base class only accepts EventHandler.
My intention is to have a "generic" delegate such that when OtherEvent inherits the methods from Event, OtherEvent still has the flexibility to use its own type of delegate with different parameters from its parent for the handler class field.
What can I do to achieve my intention?

Whats wrong with usage of default delegate EventHandler<TEventArgs>? It can be parametrized with any type, inherited from EventArgs.
Also I don't really understand purpose of Event class. You can define events simply this way:
public event EventHandler<OtherEventArgs> OtherEvent;

Perhaps use a generic instead of inheritance?
class Event<TEvent>
{
TEvent _handler;
public Event(TEvent handler)
{
_handler = handler;
}
}

Related

How to catch event firing from class constructor

Here is an example...
public class CodeGenerator
{
public delegate void GeneratorCalculatorEventHandler(decimal Fond);
public event GeneratorCalculatorEventHandler eventName;
public CodeGenerator()
{
eventName?.Invoke(0);
}
}
How could I catch the event? If I do this:
CodeGenerator CodeGen = new CodeGenerator();
CodeGen.eventName += CodeGen_eventName;
The event is fired when the handler is not subscribed to it yet. Is it possible to subscribe to an event on initialiazation?
As the comments said, this is a bad code smell. It sounds like you're doing something very unusual here and you should consider trying to find a different way to solve your problem.
One of the reasons it smells bad is: if the calling code knows to pass the handler to the constructor, then that code already knows that the constructor is being invoked. The caller could simply invoke the handler itself with the constructed instance! Events are typically for situations where something happens that the handler could not predict or control, like the user clicking a button.
I would not pass the handler to the constructor, were I in your situation. I would use a static event.
You should pass the handler to the constructor and attach it to the event, something like:
public class CodeGenerator
{
public delegate void GeneratorCalculatorEventHandler(decimal Fond);
public event GeneratorCalculatorEventHandler eventName;
public CodeGenerator(GeneratorCalculatorEventHandler listener)
{
eventName += listener;
eventName?.Invoke(0);
}
}
public class Test
{
public Test()
{
CodeGenerator gen = new CodeGenerator((sen) => { return; });
}
}

Passing an event handler as argument in C#

I'm writing down a customized Timer, that extends DispatcherTimer. It has a method called TimerSetUp, in which I associate the Tick event to an EventHandler.
public class MyTimer : DispatcherTimer
{
public MyTimer(){}
public void TimerSetUp(EventHandler<EventArgs> _EventHandler)
{
base.Tick += _EventHandler;
}
}
I have now another couple of classes, StopwatchTimer and CountdownTimer, that extend MyTimer. Let's look at CountdownTimer.
public class CountdownTimer : MyTimer
{
public CountdownTimer()
{
base.TimerSetUp(CountdownTimer_Tick);
}
public void CountdownTimer_Tick(object sender, EventArgs e)
{
//...do something
}
}
When either of them is declared, the constructor must call the base class method TimerSetUp and just set up the event handler.
The problem is that, in the signature of the TimerSetUp function, it gives me this error
Cannot implicitly convert type 'System.EventHandler<System.EventArgs>'
to'System.EventHandler<object>
And I can't get rid of it. If I just put TimerSetUp inside CountdownTimer, and just assign directly the event handler, like this
base.Tick += CountdownTimer_Tick;
it works, but I'd really like to make it work the other way.
So I'd like to know if
1) There's a way I can resolve this
2) Is this a good approach to extensibility of the code?
Nevermind, I made it. I just casted a generic object in the function signature and wrapped a new eventHandler inside the code.
public void TimerSetUp(EventHandler<object> _EventHandler)
{
base.Tick += new EventHandler<object>(_EventHandler);
}
DispatchTimer.Tick seems to be the non generic EventHandler rather than EventHandler<EventArgs>. Since you are using the base EventArgs anyway, they are functionally equivalent:
public void TimerSetUp(EventHandler _EventHandler)
{
base.Tick += _EventHandler;
}
See: https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer.tick(v=vs.110).aspx

contravariant problems with C# delegates on dictionaries

I have been developing for a while different event systems for games where the listener received a generic event-type object and had to distinguish its real type with a switch or similar and then cast to the correct subclass event.
After different aproaches I was able to get rid of the switch-case using a structure as follows (simplified):
public class Event {}
public class EventA : Event {}
public class EventB : Event {}
public delegate void HanlderDelegate(Event ev);
public class EventManager
{
Dictionary<Type, HanlderDelegate> delegateMap = new Dictionary<Type, HanlderDelegate>();
public void Dispatch(Event ev)
{
if (delegateMap.ContainsKey(ev.GetType()))
{
delegateMap[ev.GetType()].Invoke(ev);
}
}
public void Register<T>(HanlderDelegate handler) where T : Event
{
delegateMap.Add(typeof(T), handler);
}
}
public class EventListener
{
public EventListener(EventManager evtMgr)
{
evtMgr.Register<EventA>(this.HandleEventA);
evtMgr.Register<EventB>(this.HandleEventB);
}
public void HandleEventA(Event ev)
{
EventA evA = (EventA)ev;
//... do stuff
}
public void HandleEventB(Event ev)
{
EventB evB = (EventB)ev;
//... do stuff
}
}
I'm quite happy with this approach, but I still find the casting in each method improvable. I tried to make the delegate more generic
public delegate void HanlderDelegate<T>(T ev) where T : Event; so listeners could directly implement public void HandleEvent(EventA ev) and public void HandleEvent(EventB ev) and register them.
But of course, the dictionary in the EventManager class should store pointers to HanlderDelegate<Event>, and there's where problems begin, I cannot cast HanlderDelegate<EventA> to HanlderDelegate<Event> in order to store them, and at the same time casting it the other way to invoke them.
Is there a way to achieve this? I know the compiler would allow weird stuff, but I'm aware of it and can control by code that EventB is not incorrectly being casted to EventA and so on.
Thanks in advance!
You could make the delegate and the Dispatch method generic, and store the handlers as Delegate rather than HandlerDelegate<T>:
delegate void HandlerDelegate<TEvent>(TEvent ev) where TEvent : Event;
class EventManager
{
Dictionary<Type, Delegate> delegateMap = new Dictionary<Type, Delegate>();
public void Dispatch<TEvent>(TEvent ev) where TEvent : Event
{
Delegate d;
if (delegateMap.TryGetValue(typeof(TEvent), out d))
{
var handler = (HandlerDelegate<TEvent>)d;
handler(ev);
}
}
public void Register<TEvent>(HandlerDelegate<TEvent> handler) where TEvent : Event
{
delegateMap.Add(typeof(TEvent), handler);
}
}
Of course, you still have to cast in the Dispatch method, but at this point you know that the cast is correct.

Public event of base class in derived class

I have a base-class (let it be SomeBaseClass) containing a public event (SomeEvent) and I have a derived-class in which I want to raise this event but I can't(!!) VS 2010 says me (in derived-class in line: base.SomeEvent != null) "The event 'SomeBaseClass.SomeEvent' can only appear on the left hand side of += or -=". If I replace base on this It is make no sense.
No, it's absolutely right - the event is only an event (with subscription and unsubscription) as far as a derived class is concerned. If your base class wants to let derived classes raise the event, it should include a protected method to do so (typically a virtual OnFoo(EventHandler) for an event called Foo with the EventHandler type, for example). Note that if you write a field-like event in C# like this:
public event EventHandler Foo;
That's actually declaring a private field called Foo (which that class and any nested classes have access to) and a public event (which consists only of subscribe/unsubscribe). You could declare your own "custom" event like this:
protected EventHandler foo;
// Note: not thread-safe. Only present for demonstration purposes.
public event EventHandler Foo
{
add { foo += value; }
remove { foo -= value; }
}
and then derived classes would have access to the field... but I wouldn't recommend that. (I rarely declare non-private fields, other than for constants.)
You need to do it the right way (i.e., the idiomatic way in C#)
public class Base {
public event EventHandler<EventArgs> SomeEvent;
protected virtual void OnSomeEvent(EventArgs e) {
EventHandler<EventArgs> handler = SomeEvent;
if (handler != null) {
handler(this, e);
}
}
}
public class Derived {
protected virtual void OnSomeEvent(EventArgs e) {
// derived event handling here
// then invoke the base handler
base.OnSomeEvent(e);
}
}
The reason that you do it like this is because events can only be invoked from within the defining class.

In C#, how do you declare a subclass of EventHandler in an interface?

What's the code syntax for declaring a subclass of EventHandler (that you've defined) in an interface?
I create the EventHandler subclass MyEventHandler for example in the delegate declaration, but you can't declare a delegate in an interface...
When I ask Visual Studio to extract an interface it refers to the EventHandler in IMyClassName as MyClassName.MyEventHandler which obviously plays havoc with type coupling.
I'm assuming there is a simple way to do this. Do I have to explicitly declare my event handler in a separate file?
Well, you need to define the args and possibly delegate somewhere. You don't need a second file, but I'd probably recommend it... but the classes should probably not be nested, if that was the original problem.
The recommendation is to use the standard "sender, args" pattern; there are two cmmon approaches:
1: declare an event-args class separately, and use EventHandler<T> on the interface:
public class MySpecialEventArgs : EventArgs {...}
...
EventHandler<MySpecialEventArgs> MyEvent;
2: declare an event-args class and delegate type separately:
public class MySpecialEventArgs : EventArgs {...}
public delegate void MySpecialEventHandler(object sender,
MySpecialEventArgs args);
....
event MySpecialEventHandler MyEvent;
Assuming C# 2.0 or later...
public class MyEventArgs: EventArgs
{
// ... your event args properties and methods here...
}
public interface IMyInterface
{
event EventHandler<MyEventArgs> MyEvent;
}

Categories

Resources