WeakEventManager with event name lambda expression and custom event accessors - c#

I have been looking in to subscribing to an event using a weak event pattern. With the .NET 4.5 framework, we have a slick looking WeakEventManager class. Weakly subscribing to an event is as simple as
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
I'm not a big fan of 'stringly-typed' code however. I have been trying to find a way around using the string name of the event to subscribe to. The only way I have found to obtain the name of the event is using a lambda expression in the class that defines the event. In my scenario, I own the class defining the event so I can change it however I like. I have been trying to find a clean way to subscribe and unsubscribe to my event and here is what I disliked the least.
public event EventHandler<EventArgs> LoggingOn;
public event EventHandler<EventArgs> LoggingOn_Weak
{
add
{
var eventName = this.GetEventName(() => this.LoggingOn);
WeakEventManager<CurrentUser, EventArgs>.AddHandler(this, eventName, value);
}
remove
{
var eventName = this.GetEventName(() => this.LoggingOn);
WeakEventManager<CurrentUser, EventArgs>.RemoveHandler(this, eventName, value);
}
}
// In a base class view model in my scenario
private string GetEventName<T>(System.Linq.Expressions.Expression<Func<T>> expression)
{
return (expression.Body as System.Linq.Expressions.MemberExpression).Member.Name;
}
protected void OnLoggingOn(object sender, EventArgs e)
{
var handler = this.LoggingOn;
if (handler != null)
{
handler(sender, e);
}
}
Using custom event accessors I was able to avoid clunky (in my opinion) methods like LoggingOn_Subscribe(EventHandler) or adding name properties for each event. Unfortunately it is not so intuitive in that people subscribing to the event are doing so in the classic manner but have no idea other than the "_Weak" part of the name that indicates it is being subscribed to weakly.
As for my questions..
1) I have never used weak events or custom event accessors before. The code above appears to work, however, I would just like to make sure there is nothing technically wrong with it. Is there anything I'm doing here to shoot myself in the foot?
2) From a design perspective, is this a terrible idea? Are there any major design concerns I should consider? Is there better alternative? Should i just suck it up and subscribe from my subscriber using a stringly-typed event name?
Thoughts?

With .NET 4.6 you can now use the nameof() expression:
WeakEventManager<IMyGrid, MyEventArgs>.AddHandler(myGrid, nameof(IMyGrid.MouseDown), OnMouseDown);

What you could do is use the built-in System.ComponentModel.EventHandlerList. This class is a container for all of your object's event handler delegates. The primary benefit is that no storage is allocated on your object for each event unless there is actually someone subscribed to an event.
The secondary benefit is that in order to use it, you must provide a key for your event.
class MyObject
{
protected EventHandlerList Events = new EventHandlerList();
public static Event1Key = new object();
public event Event1
{
add { Events.AddHandler(Event1Key, value); }
remove { Events.RemoveHandler(Event1Key, value); }
}
}
Now you could create a variation of WeakEventManager that accepted keys rather than string names. So the consumer could say
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, EventSource.Event1Key, source_SomeEvent);

Related

How to check membership of an event handler from outside the owning class?

This question asks if there is a way to find if the code has already added its own event handler to an event. However, the answers given only work from inside the same class that own the event. (Delegate.GetInvocationList and others.)
I want to add a custom event handler to AppDomain.CurrentDomain.AssemblyResolve. Is there a way to find out if my custom handler is already added before adding it again? (For this and other standard library events.)
If the answer is indeed "That's impossible." then please give that as an answer.
That's impossible.
Basically, the only operations you have with an event from the outside are "subscribe" and "unsubscribe".
Now you could always unsubscribe before you subscribe. Unsubscribing is a no-op if the specified handler isn't already a handler for the event. If you make sure you always do that, then you'll definitely have exactly one handler subscribed. It does mean you need to be careful to do that everywhere you subscribe though - so ideally, put that code in one place.
(Alternatively, just change your event subscription so that you can easily tell that you'll only ever subscribe once...)
You can...
You can use reflection to get all delegates subscribed to the event and then check their names to see if yours is in there...
public class Foo
{
public event EventHandler MyEvent;
}
public class Bar
{
public static event EventHandler MyStaticEvent;
}
public class Test
{
public void MyDelegate(object sender, EventArgs e) { }
}
class Program
{
static void Main(string[] args)
{
Foo aFoo = new Foo();
Test aTest = new Test();
aFoo.MyEvent += aTest.MyDelegate;
FieldInfo subscribersReflect = typeof(Foo).GetField("MyEvent", BindingFlags.NonPublic | BindingFlags.Instance);
Delegate[] subscribers = (subscribersReflect.GetValue(aFoo) as MulticastDelegate).GetInvocationList();
foreach (var sub in subscribers)
Console.WriteLine(sub.Method.Name); // MyDelegate
Bar.MyStaticEvent += aTest.MyDelegate;
subscribersReflect = typeof(Bar).GetField("MyStaticEvent", BindingFlags.NonPublic | BindingFlags.Static);
subscribers = (subscribersReflect.GetValue(null) as MulticastDelegate).GetInvocationList();
foreach (var sub in subscribers)
Console.WriteLine(sub.Method.Name); // MyDelegate
Console.ReadLine();
}
}
...but you really shouldn't.
Any time you find yourself tempted to use reflection to go digging around in another class, especially a class you don't have the source for, and super-especially a framework class, that should be a warning signal that you're doing something wrong.
Jon Skeet's solution (unsubscribe then subscribe) is absolutely the correct solution to your problem, and is a good habit to be in any case. As he mentioned, unsubscribing a delegate that isn't subscribed has effectively no cost whatsoever, so any time you're unsure, go ahead and unsubscribe. It was designed that way specifically so that you could do that rather than using reflection.

Embedded Mono: How do you raise an event in C++?

I'm working on an application that's embedding Mono, and I'd like to raise an event from the C++ layer into the C# layer. Here's what I have:
void* itr(NULL);
MonoEvent* monoEvent;
while(monoEvent= mono_class_get_events(klass, &itr))
{
if(0 == strcmp(eventName, mono_event_get_name(monoEvent)))
raiseMethod = mono_event_get_raise_method(monoEvent);
}
However, raiseMethod always comes back as NULL. Looking at the structure of the MonoEvent, it looks like the add and remove methods were populated, but not the raise? Is there something special I have to do to get this to work?
EDIT: If it matters, here's the (basic) form of the delegate, class, and events I'm using in the C# layer.
public delegate void MyHandler(uint id);
public class SimpleComponent : NativeComponent
{
public event MyHandler OnEnter;
public event MyHandler OnExit;
}
May the event be defined in parent class? If so you need to traverse up the class hierarchy with something like the following:
MonoEvent* monoEvent;
while (klass)
{
void* itr = NULL;
while(monoEvent= mono_class_get_events(klass, &itr))
{
if(0 == strcmp(eventName, mono_event_get_name(monoEvent)))
raiseMethod = mono_event_get_raise_method(monoEvent);
}
klass = mono_class_get_parent(klass);
}
EDIT after comment and re-reading question:
It is normal that the raise method for event is NULL.
This method usually returns null for events declared with the C# event keyword or the Visual Basic Event keyword. This is because the C# and Visual Basic compilers do not generate such a method by default.
(source)
I am afraid it may be hard to fire an event of a class. Because it is actually breaking the concept of events in .NET - which says that the class itself can only fire its own Event. Actually, even from C# it is hard to raise the event of other class.
Conceptually, events are pair of add_handler and remove_handler methods where you specify delegates to be called when event's circumstances occur. It is up to class how it implements events. Technically, it is just a private delegate field, AFAIK.
You may try to locate it.
I am not sure if it is a proper approach, but one of the answers in How do I raise an event via reflection in .NET/C#? describes how to raise event using reflection. You might attempt to convert it into mono_class / mono_field calls, etc.
Krizz's answer is the most complete. This is how I fixed my code to work how I would "expect".
I changed the C# side to:
public delegate void MyHandler(uint aEntityId);
public class SimpleComponent: NativeComponent
{
public event MyHandler OnEnter;
public event MyHandler OnExit;
protected void CallOnEnter(uint aEntityId)
{
if (OnEnter != null)
OnEnter(aEntityId);
}
protected void CallOnExit(uint aEntityId)
{
if (OnExit!= null)
OnExit(aEntityId);
}
}
Then grabbed the mono method with
raiseMethod = mono_class_get_method_from_name(klass, "CallOnEnter", 1);

Short way to write an event?

Typically we use this code:
private EventHandler _updateErrorIcons;
public event EventHandler UpdateErrorIcons
{
add { _updateErrorIcons += value; }
remove { _updateErrorIcons -= value; }
}
Is there a similar shortcut like with automatic properties?
Something like:
public event EventHandler UpdateErrorIcons { add; remove; }
Yep. Get rid of the { add; remove; } part and the backing delegate field and you're golden:
public event EventHandler UpdateErrorIcons;
That's it!
Let me just add that before you asked this question, I hadn't even thought about the fact that the auto-implemented version of events is inconsistent with that of properties. Personally, I would actually prefer it if auto-implemented events worked the way you first attempted in your question. It would be more consistent, and it would also serve as a mental reminder that events are not identical to delegate fields, just like properties are not identical to regular fields.
Honestly, I think you're the rare exception where you actually knew about the custom syntax first. A lot of .NET developers have no clue there's an option to implement your own add and remove methods at all.
Update: Just for your own peace of mind, I have confirmed using Reflector that the default implementation of events in C# 4 (i.e., the implementation that gets generated when you go the auto-implemented route) is equivalent to this:
private EventHandler _updateErrorIcons;
public event EventHandler UpdateErrorIcons
{
add
{
EventHandler current, original;
do
{
original = _updateErrorIcons;
EventHandler updated = (EventHandler)Delegate.Combine(original, value);
current = Interlocked.CompareExchange(ref _updateErrorIcons, updated, original);
}
while (current != original);
}
remove
{
// Same deal, only with Delegate.Remove instead of Delegate.Combine.
}
}
Note that the above utilizes lock-free synchronization to effectively serialize add and remove calls. So if you're using the latest C# compiler, you don't need to implement add/remove yourself even for synchronization.
public event EventHandler UpdateErrorIcons;
is just fine
you can use
yourObbject.UpdateErrorIcons += YourFunction;
add {} and remove {} are used only in special cases where you need to handle event hookups manually. Us mere mortals normally just use public event EventHandler UpdateErrorIcons; where "EventHandler" is the delegate of choice.
For instance:
public delegate void MyEventDelegate(object sender, string param1);
public event MyEventDelegate MyEvent;
Note that because MyEvent is null if it doesn't have any listeners you need to check if it is null before invoking it. A standard method for doing this check is:
public void InvokeMyEvent(string param1)
{
MyEventDelegate myEventDelegate = MyEvent;
if (myEventDelegate != null)
myEventDelegate(this, param1);
}
A key element in this check is to make a copy of the object in question first and then work only on the copy. If not you could get a rare race condition where another thread unhooks between your if and your call.

Explain code for custom event in user control

Someone gave me this code that works great. But I would really like to understand what is happening inside it. Could somebody explain please? What is the meaning of each part of the code? The code is inside a custom control which has two labels inside a panel.
Also I've seen some custom control events that use add/remove syntax, what is that for? What is the difference with what is happening here?
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public event EventHandler MyCustomClickEvent;
protected virtual void OnMyCustomClickEvent(EventArgs e)
{
// Here, you use the "this" so it's your own control. You can also
// customize the EventArgs to pass something you'd like.
if (MyCustomClickEvent != null)
MyCustomClickEvent(this, e);
}
private void label1_Click(object sender, EventArgs e)
{
OnMyCustomClickEvent(EventArgs.Empty);
}
}
See my comments below. Also for a more detailed event I blogged on this concept a while back where I go into more detail on the entire process.
public partial class UserControl1 : UserControl
{
//This is the standard constructor of a user control
public UserControl1()
{
InitializeComponent();
}
//This defines an event called "MyCustomClickEvent", which is a generic
//event handler. (EventHander is a delegate definition that defines the contract
//of what information will be shared by the event. In this case a single parameter
//of an EventArgs object.
public event EventHandler MyCustomClickEvent;
//This method is used to raise the event, when the event should be raised,
//this method will check to see if there are any subscribers, if there are,
//it raises the event
protected virtual void OnMyCustomClickEvent(EventArgs e)
{
// Here, you use the "this" so it's your own control. You can also
// customize the EventArgs to pass something you'd like.
if (MyCustomClickEvent != null)
MyCustomClickEvent(this, e);
}
private void label1_Click(object sender, EventArgs e)
{
OnMyCustomClickEvent(EventArgs.Empty);
}
}
I'd recommend reading up on Events for C# on MSDN. This is covered in detail.
Basically, MyCustomClickEvent is an event. The OnMyCustomClickEvent method is used to raise the event, but is being done in a way that subclasses can also raise this event if required.
When you click on "label1", the OnMyCustomClickEvent method runs, which raises the event. Any delegates subscribed to the event will execute at that point.
You mentioned seeing the add/remove syntax for events in some custom control examples. Most likely those examples are using the UserControl class' Events property to store event handlers, such as in the following example:
public event EventHandler MyEvent
{
add
{
Events.AddHandler("MyEvent", value);
}
remove
{
Events.RemoveHandler("MyEvent", value);
}
}
The idea there is that usually a consumer of a control is not going to want to handle every single event that the control exposes. If each event is defined as a "field" event (as in your example), then each event will take up a chunk of memory even if there are no subscribers for that event. When you have a complex page constructed of hundreds of controls, each of which may have dozens of events, the memory consumption for unused events is not insignificant.
This is why the System.ComponentModel.Component class (the base class of the System.Windows.Forms.Control class) has an Events property, which is basically a dictionary to store event handler delegates. This way each event is implemented more like a property than a field. The add/remove handlers for each event store or remove delegates from the Events dictionary. If an event is not used, then there just isn't an entry in the Events dictionary for it, and no additional memory is consumed for that event. It's a trade-off of doing slightly more work (having to look up the event handler) to save slightly more memory.
EDIT: fixed my answer to pertain to Windows Forms, rather than ASP.NET, although the concepts are the same.
Concerning the add/remove, this is a "manual" implementation of events. The following two snippets do the same thing.
Automatic implementation:
public event EventHandler MyEvent;
Manual implementation:
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add { _myEvent += value; }
remove { _myEvent -= value; }
}
This is exactly the same idea as automatic properties where:
public string Property { get; set; };
Does exactly the same as:
private string _property;
public string Property
{
get { return _property; }
set { _property = value; }
}
The difference between these snippets is that with the manual implementations, you get more control. Examples are:
Implement logic in the add/get and remove/set;
Get access to the fields which allows you to set e.g. [NonSerializable];
Put the values in e.g. a Dictionary.
The Form class e.g. does the latter to keep the number of fields in the Form class down.

C# pattern to prevent an event handler hooked twice [duplicate]

This question already has answers here:
How to ensure an event is only subscribed to once
(8 answers)
Closed 8 years ago.
Duplicate of: How to ensure an event is only subscribed to once
and Has an event handler already been added?
I have a singleton that provides some service and my classes hook into some events on it, sometimes a class is hooking twice to the event and then gets called twice.
I'm looking for a classical way to prevent this from happening. somehow I need to check if I've already hooked to this event...
How about just removing the event first with -= , if it is not found an exception is not thrown
/// -= Removes the event if it has been already added, this prevents multiple firing of the event
((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii);
((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii);
Explicitly implement the event and check the invocation list. You'll also need to check for null:
using System.Linq; // Required for the .Contains call below:
...
private EventHandler foo;
public event EventHandler Foo
{
add
{
if (foo == null || !foo.GetInvocationList().Contains(value))
{
foo += value;
}
}
remove
{
foo -= value;
}
}
Using the code above, if a caller subscribes to the event multiple times, it will simply be ignored.
I've tested each solution and the best one (considering performance) is:
private EventHandler _foo;
public event EventHandler Foo {
add {
_foo -= value;
_foo += value;
}
remove {
_foo -= value;
}
}
No Linq using required. No need to check for null before cancelling a subscription (see MS EventHandler for details). No need to remember to do the unsubscription everywhere.
You really should handle this at the sink level and not the source level. That is, don't prescribe event handler logic at the event source - leave that to the handlers (the sinks) themselves.
As the developer of a service, who are you to say that sinks can only register once? What if they want to register twice for some reason? And if you are trying to correct bugs in the sinks by modifying the source, it's again a good reason for correcting these issues at the sink-level.
I'm sure you have your reasons; an event source for which duplicate sinks are illegal is not unfathomable. But perhaps you should consider an alternate architecture that leaves the semantics of an event intact.
You need to implement the add and remove accessors on the event, and then check the target list of the delegate, or store the targets in a list.
In the add method, you can use the Delegate.GetInvocationList method to obtain a list of the targets already added to the delegate.
Since delegates are defined to compare equal if they're linked to the same method on the same target object, you could probably run through that list and compare, and if you find none that compares equal, you add the new one.
Here's sample code, compile as console application:
using System;
using System.Linq;
namespace DemoApp
{
public class TestClass
{
private EventHandler _Test;
public event EventHandler Test
{
add
{
if (_Test == null || !_Test.GetInvocationList().Contains(value))
_Test += value;
}
remove
{
_Test -= value;
}
}
public void OnTest()
{
if (_Test != null)
_Test(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
TestClass tc = new TestClass();
tc.Test += tc_Test;
tc.Test += tc_Test;
tc.OnTest();
Console.In.ReadLine();
}
static void tc_Test(object sender, EventArgs e)
{
Console.Out.WriteLine("tc_Test called");
}
}
}
Output:
tc_Test called
(ie. only once)
Microsoft's Reactive Extensions (Rx) framework can also be used to do "subscribe only once".
Given a mouse event foo.Clicked, here's how to subscribe and receive only a single invocation:
Observable.FromEvent<MouseEventArgs>(foo, nameof(foo.Clicked))
.Take(1)
.Subscribe(MyHandler);
...
private void MyHandler(IEvent<MouseEventArgs> eventInfo)
{
// This will be called just once!
var sender = eventInfo.Sender;
var args = eventInfo.EventArgs;
}
In addition to providing "subscribe once" functionality, the RX approach offers the ability to compose events together or filter events. It's quite nifty.
Create an Action instead of an event. Your class may look like:
public class MyClass
{
// sender arguments <----- Use this action instead of an event
public Action<object, EventArgs> OnSomeEventOccured;
public void SomeMethod()
{
if(OnSomeEventOccured!=null)
OnSomeEventOccured(this, null);
}
}
have your singleton object check it's list of who it notifies and only call once if duplicated. Alternatively if possible reject event attachment request.
In silverlight you need to say e.Handled = true; in the event code.
void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true; //this fixes the double event fire problem.
string name = (e.OriginalSource as Image).Tag.ToString();
DoSomething(name);
}
Please tick me if this helps.

Categories