I know that the purpose of event keyword just to used in pair with += operator to make list of delegate. And when constructing delegate we can make any signature (of parameter) for method that compatible with that delegate.
For example I create delegate
public delegate void StartEventHandler(object sender, StartEventArgs e);
with two parameter: the first with the type object and the second with the type StartEventArgs. But in many article that I found on the internet, the second parameter for that delegate must inherited EventArgs type. Why we do this instead to make the second parameter come/inherited from arbitrary type?
You don't need to and I'm sure the code will still compile if you used an arbitrary base class but it is a convention.
Conventions are good because it makes it easier to understand for people who are not familiar with your code already. If I subscribe to an event in C# I expect a certain method signature.
It is also good because it makes different types of events interchangeable.
For example, say you have three delegates
public delegate void AEventHandler(object sender, AEventArgs e);
public delegate void BEventHandler(object sender, BEventArgs e);
public delegate void CEventHandler(object sender, CEventArgs e);
You could write a generic method that conforms to all three delegates because all the args objects inherit from the same base class.
public void eventMethod(object sender, EventArgs e) {
// Any one of the events fired
}
public void subscribeToEvents() {
eventA += new AEventHandler(this.eventMethod);
eventB += new BEventHandler(this.eventMethod);
eventC += new CEventHandler(this.eventMethod);
}
And even cast if you know what types to expect
public void eventMethod(object sender, EventArgs e) {
// Any one of the events fired
if (e is BEventArgs) {
// Event type B fired
var eventB = e as BEventArgs;
eventB.doSomething()
}
}
Related
I have a class Character, that stores 3 CharacterAttribute classes. What I want is that at a certain point Character would do something if an event is raised within one of the CharacterAttribute classes.
class Character
{
public CharacterAttribute Physical;
public Character Create(...)
{
Physical = new CharacterAttribute();
Physical.SkillPointGained += this.OnSkillPointGained();
// ERROR: cannot convert void to System.EventHandler
}
public void OnSkillPointGained()
{
// do stuff here after the event has been fired
}
}
class CharacterAttribute
{
public event EventHandler SkillPointGained;
public void GainExperience(...)
{
SkillPointGained(this, new EventArgs()); // would raise the event here
}
}
I get a "cannot implicitly convert void to System.EventHandler" error. What am I missing?
Two lines of code need to be fixed up a little bit.
public void OnSkillPointGained()
should read:
void OnSkillPointGained(object sender, EventArgs e)
And
Physical.SkillPointGained += this.OnSkillPointGained();
Should read:
Physical.SkillPointGained += this.OnSkillPointGained;
(...however I like the elegance of Marc's answer better.)
EventHandler has the delegate signature of void EventHandler(object sender, EventArgs args). You are a: matching the wrong signature, and b: you're invoking the method rather than targeting the method as a delegate.
You can cheat, though:
Physical.SkillPointGained += delegate { this.OnSkillPointGained(); };
This creates an anonymous method that acts as the immediate handler, drops the sender/args, and invokes your OnSkillPointGained method.
The documentation for EventHandler<TEventArgs> says:
The second parameter is a type derived from EventArgs and supplies
any fields or properties needed to hold the event data.
and it seems to be generally recommended throughout the .Net documentation.
However it turns out that I can do the following which works just fine:
public event EventHandler<int> Panned;
and invoke the event handler as:
int value = 10;
if (Panned != null)
{
Panned(this, value);
}
and on the observer side:
subject.Panned += (sender, e) =>
{
Console.WriteLine(e);
};
To me this seems better than littering the code with little classes that inherit from EventArgs or having a generic EventArgs as proposed by Does .NET have a built-in EventArgs<T>?
So why is it required that I inherit the EventHandler generic argument from EventArgs?
If all you need to do is pass an int to the handler then what you are doing is fine.
It used to be the case (before .NET 4.5) that the EventHandler type argument TEventArgs was constrained to inherit from EventArgs but not anymore:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
The fact that MS dropped the constraint should tell you that they were being too strict and what you are doing is fine.
In the case that you need to pass a complex type to the handler then you might aswell inherit EventArgs for reasons of polymorphism. Also, the EventArgs.Empty member is useful.
This is just a convention. In fact, you don't even have to use the EventHandler<> generic delegate. You could have:
public event Action SomeEvent;
public void OnAction()
{
var a = this.SomeEvent;
if (a != null)
{
a();
}
}
Of course, the convention is there for a reason. As far as I know, every standard .NET event follows the pattern of using a void-returning delegate that takes an object parameter and a second parameter of EventArgs or a derived type. This makes it easy to use these events without having to refer to the documentation each time.
Silly, Silly, Silly!
Will this work?...
class Program
{
public static event Func<int> SomeEvent;
static void Main(string[] args)
{
SomeEvent += () => 7;
SomeEvent += () => 8;
var a = SomeEvent();
Console.WriteLine(a);
}
}
I tried it: it does! Of course, it's very odd to have an event where the delegate has a return value, because it's not obvious which handler's value will be returned to the caller if there are multiple attached handlers. In the above example, it turns out that 8 is written to the console.
Interesting but, I suspect, useless ;-)
Would You Ever Use This?
I don't think it would ever be sensible to have a non-void returning delegate type, as in my example. However, you might consider using a delegate whose parameters are value types (structs, not classes) for performance reasons. It might be possible to use events without incurring the garbage collection penalty of allocating EventArgs objects on the heap.
I use the article here to implement the weak referenced handler pattern.
The code is really perfect before I found that the event handler in WPF is not the instance of EventHandler. Such as PropertyChangedEventHandler:
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
Although I think they have the same signature as EventHandler<PropertyChangedEventArgs>, where EventHandler is :
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
I try to convert it directly but the C# said "I cannot do it".
So my question is it possible to smoothly convert these two kind of delegate?
What's the different between them?
I have some delegates with type parameters, and I convert them like this:
public void AddHandler(EventHandlerUntyped<TKey> EventHandlerMethod)
{
SomeEventTakingTypedEventHandler += new TypedEventHandler<TKey, TValue>(EventHandlerMethod);
}
These are the delegates:
public delegate void TypedEventHandler<TKey, TValue>(object sender, TypedEventArgs<TKey, TValue> ItemInfo);
public delegate void EventHandlerUntyped<TKey>(object sender, EventArgsUntyped<TKey> ItemInfo);
PS: The Typed Event Args inherits the untyped event args.
I know it's not exactly what you want, but I think it's a good hint.
You can ignore the TKey and try something like:
new EventHandler<PropertyChangedEventArgs>(SomeMethodUsingPropertyChangedEventHandlerSignature).
new PropertyChangedEventHandler(SomeMethodUsingCommonEventHandlerSignature);
One thing that annoys me is that in a default Event, Sender is of type object and therefore almost always requires a manual cast before we can use it. Luckily since VB now also supports variance in delegates, we can update the signature of an event in such a way that the sender is strongly typed, see: Event parameter; "sender as Object", or "sender as T"?
Unfortunatly this doesn't work for existing declared events which senders are of type object.
Now one solution would be ofcourse to generate a fake EventHandler which internally takes care of the cast for you. I made a quick example, see:
struct EventHandler<TSender, TEventArgs>
where TEventArgs: EventArgs
{
private readonly Action<TSender, TEventArgs> _delegate;
public EventHandler(Action<TSender, TEventArgs> #delegate)
{
if (#delegate == null)
throw new ArgumentNullException("#delegate");
_delegate = #delegate;
}
public static implicit operator EventHandler<TEventArgs>(EventHandler<TSender, TEventArgs> eventHandler)
{
return new EventHandler<TEventArgs>(eventHandler.Execute);
}
private void Execute(object sender, EventArgs e)
{
TSender typedSender = (TSender)sender;
TEventArgs typedEventArgs = (TEventArgs)e;
_delegate(typedSender, typedEventArgs);
}
}
which can be used as you would expect it to be used:
class Program
{
event EventHandler<EventArgs> Test;
static void Main(string[] args)
{
new Program().Main();
}
void Main()
{
Test += new EventHandler<Program, EventArgs>(TestEventHandler);
Test(this, EventArgs.Empty);
}
void TestEventHandler(Program sender, EventArgs e)
{
throw new NotImplementedException();
}
}
Now if I really want to use this, there is alot of work to be done. (The struct should behave just like the original delegate). Yet i do have the feeling that there is either already a great implementation out there, or there is no implementation since there are some major drawbacks i overlooked.
Who can answer me the above question? Any other hints?
I can't think of any solution that would have less code than
var original = (OriginalType)sender;
Also if class is yours nothing stops you from creating your own delegate instead of EventHandler delegate
delegate void EventHandler<in TSender, in TArgs>(TSender sender, TArgs args);
This is contravariant in args and sender
People usually also have the reference of the object for which the event is raised and therefore they rarely need to get the thing out of the sender. Could it be possible in your case?
Edit: Since you mentioned you're dealing with events that are not written by you so modifying the delegate is out of question and you probably don't have reference to the object so you have to resort to the sender in that case. Now in your solution you can not unsubscribe to the event. If you modify it to support that then you'll have to keep the reference to this struct of yours which is more work than simple casting. I think casting is still the cleanest solution.
I'm of the opinion that using methods as event handlers is now bad OOP practice. Now that we have anonymous delegates we just don't need to make methods just to handle events. Creating an event handler method that could get called by every other method in a class is much like making class members public and letting any class call anything.
In the past we have had to write this:
public class Program
{
public void Main()
{
var button = new Button();
button.Click += button_Click;
}
void button_Click(object sender, EventArgs e)
{
var button = (Button)sender; //Need to cast here
}
}
But now we can write this:
public class Program
{
public void Main()
{
var button = new Button();
button.Click += (sender, e) =>
{
//Can use `button` here
//Just ignore `sender`
};
}
}
Using anonymous delegates allows us to use the event "sender" reference directly without the need of any casting.
Much cleaner object oriented coding.
Assuming we declare a class DerivedEventArgs:
public class DerivedEventArgs : EventArgs { ... }
then EventHandler delegate is able to accept methods with the following signature:
public static void Some_Method(object o, DerivedEventArgs e) { ... }
But if we try to subscribe a method with the above signature to the event implementing EventHandler delegate:
public event EventHandler MyEvent;
, then we get an error. Why is that?
thanx
Suppose the code raising the event specified a value which wasn't a DerivedEventArgs - what would you expect it to do? Basically you'd lose type safety.
EDIT Note that you can do it the other way round - you can subscribe to an event with a more specific parameter type using a method with a less specific parameter type - because the event is still guaranteeing that it will call the handler with something compatible. Here's an example:
using System;
class Test
{
public class DerivedEventArgs : EventArgs { }
public EventHandler<DerivedEventArgs> SpecialistEvent;
static void Main()
{
Test t = new Test();
t.SpecialistEvent += GeneralHandler;
}
static void GeneralHandler(object sender, EventArgs e)
{
}
}
MyEvent(this, EventArgs.Empty)
would try to pass a regular EventArgs to DerivedEventArgs in
Some_Method(object o, DerivedEventArgs e)
and then if Some_Method tried to do an
e.PropertyInDerivedClass
it'd fail.
try using an EventHandler<T> for your event, where T is your DerivedEventArgs class.
From MSDN EventHandler documentation...
EventHandler is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must supply your own custom event data type and either create a delegate where the type of the second parameter is your custom type, or use the generic EventHandler< TEventArgs > delegate class and substitute your custom type for the generic type parameter.
The EventHandler Delegate was not made to do what you're trying to do. It's a simple convenience delegate for use in cases where event data is unimportant. The event that utilizes this delegate type may send a EventArgs with the least derived event argument type possible. That means your delegate should be ready to receive anything.
But in your example, you're specifying a more derived event argument type in your handler. Hence, if allowed by the compiler, your code can actually receive another EventArgs type that's not what you expect but defined as your derived type; breaking type safety as mentioned earlier.