How do C# Events work behind the scenes? - c#

I'm using C#, .NET 3.5. I understand how to utilize events, how to declare them in my class, how to hook them from somewhere else, etc. A contrived example:
public class MyList
{
private List<string> m_Strings = new List<string>();
public EventHandler<EventArgs> ElementAddedEvent;
public void Add(string value)
{
m_Strings.Add(value);
if (ElementAddedEvent != null)
ElementAddedEvent(value, EventArgs.Empty);
}
}
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
MyList tmp = new MyList();
tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired);
tmp.Add("test");
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
However, what I do not understand, is when one declares an event handler
public EventHandler<EventArgs> ElementAddedEvent;
It's never initialized - so what, exactly, is ElementAddedEvent? What does it point to? The following won't work, because the EventHandler is never initialized:
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
EventHandler<EventArgs> somethingHappend;
somethingHappend += new EventHandler<EventArgs>(Fired);
somethingHappend(this, EventArgs.Empty);
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
I notice that there is an EventHandler.CreateDelegate(...), but all the method signatures suggest this is only used for attaching Delegates to an already existing EventHandler through the typical ElementAddedEvent += new EventHandler(MyMethod).
I'm not sure if what I am trying to do will help... but ultimately I'd like to come up with an abstract parent DataContext in LINQ whose children can register which table Types they want "observed" so I can have events such as BeforeUpdate and AfterUpdate, but specific to types. Something like this:
public class BaseDataContext : DataContext
{
private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>();
public static void Observe(Type type)
{
if (m_ObservedTypes.ContainsKey(type) == false)
{
m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>());
EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler);
}
}
public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events
{
get { return m_ObservedTypes; }
}
}
public class MyClass
{
public MyClass()
{
BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);
}
public void OnUserUpdated(object sender, EventArgs args)
{
// do something
}
}
Thinking about this made me realize I don't really understand what's happening under the hod with events - and I would like to understand :)

I've written this up in a fair amount of detail in an article, but here's the summary, assuming you're reasonably happy with delegates themselves:
An event is just an "add" method and a "remove" method, in the same way that a property is really just a "get" method and a "set" method. (In fact, the CLI allows a "raise/fire" method as well, but C# never generates this.) Metadata describes the event with references to the methods.
When you declare a field-like event (like your ElementAddedEvent) the compiler generates the methods and a private field (of the same type as the delegate). Within the class, when you refer to ElementAddedEvent you're referring to the field. Outside the class, you're referring to the field.
When anyone subscribes to an event (with the += operator) that calls the add method. When they unsubscribe (with the -= operator) that calls the remove.
For field-like events, there's some synchronization but otherwise the add/remove just call Delegate.Combine/Remove to change the value of the auto-generated field. Both of these operations assign to the backing field - remember that delegates are immutable. In other words, the autogenerated code is very much like this:
// Backing field
// The underscores just make it simpler to see what's going on here.
// In the rest of your source code for this class, if you refer to
// ElementAddedEvent, you're really referring to this field.
private EventHandler<EventArgs> __ElementAddedEvent;
// Actual event
public EventHandler<EventArgs> ElementAddedEvent
{
add
{
lock(this)
{
// Equivalent to __ElementAddedEvent += value;
__ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value);
}
}
remove
{
lock(this)
{
// Equivalent to __ElementAddedEvent -= value;
__ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value);
}
}
}
The initial value of the generated field in your case is null - and it will always become null again if all subscribers are removed, as that is the behaviour of Delegate.Remove.
If you want a "no-op" handler to subscribe to your event, so as to avoid the nullity check, you can do:
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
The delegate {} is just an anonymous method which doesn't care about its parameters and does nothing.
If there's anything that's still unclear, please ask and I'll try to help!

Under the hood, events are just delegates with special calling conventions. (For example, you don't have to check for nullity before raising an event.)
In pseudocode, Event.Invoke() breaks down like this:
If Event Has Listeners
Call each listener synchronously on this thread in arbitrary order.
Since events are multicast, they will have zero or more listeners, held in a collection. The CLR will loop through them, calling each in an arbitrary order.
One big caveat to remember is that event handlers execute in the same thread as the event is raised in. It's a common mental error to think of them as spawning a new thread. They do not.

Related

How to assign the same method to multiple delegates

Is it possible to assign the same method to multiple delegates all at once?
public class Hoge
{
public event Action EventA;
public event Action EventB;
public event Action EventC;
public Hoge()
{
EventA += () => FugaMethod();
EventB += () => FugaMethod();
EventC += () => FugaMethod();
}
private void FugaMethod()
{
Console.WriteLine("Hello.");
}
}
I'd like to simplify the assignments of the FugaMethod().
since events can't go as a parameter ... unfortunately ... no
if we are talking about a shitload of events, reflection would be the way to go ... but that i'd not really call "simplification"
edit:
for clarification:
what you can pass as a parameter:
the current list of eventhandlers attached to a static or specific event on a specific object (what you pass in this case is not the event, but a MulticastDelegate)
what you can not pass as a parameter: the event itself ...
you are not able to pass an event in a direct way that would allow to attach another event handler ... for that you would probably want to pass the specific obect instance, the EventInfo describing the event itself, and the new handler(s) ...
regarding "simplification":
what you need to do would be:
-use reflection to obtain the EventInfo objects of the desired events
-for each instance and each EventInfo call EventInfo.AddEventHandler, passing the instance as target and the eventhandler as handler
since you cannot pass the event as a parameter, you cannot extract a simple and typesafe method to get the desired EventInfo objects. you will have to use some selection by name, or other logic that takes the instance type apart by using Type.getEvent or Type.getEvents
so, if you are not handling a shitload of events, like a few hundred, writing it like you did seems to be the preferable way to go ... the reflection approach will be anything but simpler or shorter
There is a way to assign multiple event handlers at once that does not require reflection however itäs not trivial and some programming is necessary. You can use a dictionary to store your events if you want initialize them in a loop.
As a side note: by convention the event keyword should only be used if the delegate is of type EventHandler. It may and probably will confuse others when they try to use it.
Hoge class:
public class Hoge
{
// A dictionary to store your events.
private Dictionary<string, EventHandler> events = new Dictionary<string, EventHandler>()
{
{ "EventA", null },
{ "EventB", null },
{ "EventC", null }
};
// Event add/remove accessors.
public event EventHandler EventA
{
add
{
lock (events)
{
events["EventA"] += (EventHandler)events["EventA"] + value;
}
}
remove
{
lock (events)
{
events["EventA"] += (EventHandler)events["EventA"] - value;
}
}
}
// You can do the same for other events.
public event EventHandler EventB;
public event EventHandler EventC;
public Hoge()
{
// Initialize events in a loop.
foreach (var key in events.Keys.ToList())
{
events[key] += FugaMethod;
}
}
// Raises EventA.
public void RaiseEventA()
{
EventHandler handler;
if (null != (handler = (EventHandler)events["EventA"]))
{
handler(this, EventArgs.Empty);
}
}
// Event handler.
private void FugaMethod(object sender, EventArgs e)
{
Console.WriteLine("Hello.");
}
}
Usage:
class Program
{
static void Main(string[] args)
{
new Hoge().RaiseEventA();
}
}
How to: Use a Dictionary to Store Event Instances (C# Programming Guide)
Handling and Raising Events

Declare EventHandlers as Static or Non-Static in WPF Controls

I was working on creating a custom control with Command behavior and came across something odd. Some articles I found declared the CanExecuteChangedHandler EventHandler as static and others were non-static. Microsoft's SDK documentation shows static but when I declare it as static I get odd behavior when using multiple controls.
private static EventHandler canExecuteChangedHandler;
private void AddSecureCommand(ISecureCommand secureCommand)
{
canExecuteChangedHandler = new EventHandler(CanExecuteChanged);
securityTypeChangedHandler = new EventHandler(SecurityTypeChanged);
if (secureCommand != null)
{
secureCommand.CanExecuteChanged += canExecuteChangedHandler;
secureCommand.SecurityTypeChanged += securityTypeChangedHandler;
}
}
Does anyone know the proper way? Am I doing something wrong that is causing the static EventHandler not to work?
The stated reason for keeping a local copy of EventHandler is that the WPF commanding sub-system uses weak references internally and therefore we need to keep a reference to the specific delegate object that is added to the CanExecuteChanged event. If fact, anytime we are adding to any commanding sub-system event, we should also observe this practice, as you have for SecurityTypeChanged.
The short answer to your question is that canExecuteChangedHandler can be static, but you must be careful to only initialize it once. The reason it can be static is that all new EventHandler(CanExecuteChanged) will do the same thing if CanExecuteChanged is static. The reason to initialize it once is that different instances are different.
A private property that has the right read-only semantics is:
static EventHandler canExecuteChangedHandler
{
get
{
if (internalCanExecuteChangedHandler == null)
internalCanExecuteChangedHandler = new EventHandler(CanExecuteChanged);
return internalCanExecuteChangedHandler;
}
}
static EventHandler internalCanExecuteChangedHandler;
but this only works if CanExecuteChanged is static. If it is not, then remove the static qualifiers. In either case you have to be careful to actually use the property.
In this particular example, the second time that AddSecureCommand is called the first canExecuteChangedHandler is at risk of being garbage collected.
Finally, if this all sounds like black-magic, here is a code example to show what is happening.
public class Container
{
private WeakReference reference;
public object Object
{
get { return reference.IsAlive ? reference.Target : null; }
set { reference = new WeakReference(value); }
}
}
public class DelegateTest
{
private EventHandler eventHandler;
private Container container1;
private Container container2;
void MyEventHandler(object sender, EventArgs args)
{
}
public DelegateTest()
{
this.eventHandler = new EventHandler(MyEventHandler);
this.container1 = new Container { Object = this.eventHandler };
this.container2 = new Container { Object = new EventHandler(MyEventHandler) };
GC.Collect();
Console.WriteLine("container1: {0}", this.container1.Object == null);
Console.WriteLine("container2: {0}", this.container2.Object == null);
}
}
This produces this output:
container1: False
container2: True
which indicates that during the garbage collection that the second container had its EventHandler garbage-collected "out from underneath it". This is by design the way that weak references work and the explanation for you need to keep a reference to it yourself.

In C#, why can't I test if a event handler is null anywhere outside of the class that it's defined?

I am sure that I am just not understanding something fundamental about events and/or delegates in C#, but why can't I do the Boolean tests in this code sample:
public class UseSomeEventBase {
public delegate void SomeEventHandler(object sender, EventArgs e);
public event SomeEventHandler SomeEvent;
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
}
public class UseSomeEvent : UseSomeEventBase {
public bool IsSomeEventHandlerNull() {
// "LEFT HAND SIDE" COMPILER ERROR
return SomeEvent == null;
}
}
class Program {
static void Main(string[] args) {
var useSomeEvent = new UseSomeEvent();
useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEvent.SomeEvent == null) {
}
var useSomeEventBase = new UseSomeEventBase();
useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEventBase.SomeEvent == null) {
}
}
static void FuncToHandle(object sender, EventArgs e) { }
}
An event is really just an "add" operation and a "remove" operation. You can't get the value, you can't set the value, you can't call it - you can just subscribe a handler for the event (add) or unsubscribe one (remove). This is fine - it's encapsulation, plain and simple. It's up to the publisher to implement add/remove appropriately, but unless the publisher chooses to make the details available, subscribers can't modify or access the implementation-specific parts.
Field-like events in C# (where you don't specify the add/remove bits) hide this - they create a variable of a delegate type and an event. The event's add/remove implementations just use the variable to keep track of the subscribers.
Inside the class you refer to the variable (so you can get the currently subscribed delegates, execute them etc) and outside the class you refer to the event itself (so only have add/remove abilities).
The alternative to field-like events is where you explicitly implement the add/remove yourself, e.g.
private EventHandler clickHandler; // Normal private field
public event EventHandler Click
{
add
{
Console.WriteLine("New subscriber");
clickHandler += value;
}
remove
{
Console.WriteLine("Lost a subscriber");
clickHandler -= value;
}
}
See my article on events for more information.
Of course the event publisher can also make more information available - you could write a property like ClickHandlers to return the current multi-cast delegate, or HasClickHandlersto return whether there are any or not. That's not part of the core event model though.
You can easily use a very simple approach here to not repeatedly subscribe to an event.
Either of the 2 approaches below can be used:
Flag approach : _getWarehouseForVendorCompletedSubscribed is a private variable initialized to false.
if (!_getWarehouseForVendorCompletedSubscribed)
{
_serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
_getWarehouseForVendorCompletedSubscribed = true;
}
Unsubscribe Approach :Include an unsubscribe everytime you want to subscribe.
_serviceClient.GetWarehouseForVendorCompleted -= new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
_serviceClient.GetWarehouseForVendorCompleted += new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
Here the answer:
using System;
delegate void MyEventHandler();
class MyEvent
{
string s;
public event MyEventHandler SomeEvent;
// This is called to raise the event.
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent();
}
}
public string IsNull
{
get
{
if (SomeEvent != null)
return s = "The EventHandlerList is not NULL";
else return s = "The EventHandlerList is NULL"; ;
}
}
}
class EventDemo
{
// An event handler.
static void Handler()
{
Console.WriteLine("Event occurred");
}
static void Main()
{
MyEvent evt = new MyEvent();
// Add Handler() to the event list.
evt.SomeEvent += Handler;
// Raise the event.
//evt.OnSomeEvent();
evt.SomeEvent -= Handler;
Console.WriteLine(evt.IsNull);
Console.ReadKey();
}
}
Here's a slightly different question
What value is there in testing an externally defined event for null?
As an external consumer of an event you can only do 2 operations
Add a handler
Remove a handler
The null or non-nullness of the event has no bearing on these 2 actions. Why do you want to run a test which provides no perceivable value?
It's a rule in place when using the 'event' keyword. When you create an event, you are restricting outside class interaction with the delegate to a "subscribe / unsubscribe" relationship, this includes cases of inheritance. Remember an event is essentially a property, but for method calls, it isn't really an object itself, so really it looks more like this:
public event SomeEventHandler SomeEvent
{
add
{
//Add method call to delegate
}
remove
{
//Remove method call to delegate
}
}
You'd have to do that from the base class. That's the exact reason that you did this:
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
You can't access events from a derived class. Also, you should make that method virtual, so that it can be overridden in a derived class.
Publisher of the event implicitly overload only += and -= operations, and other operations are not implemented in the publisher because of the obvious reasons as explained above, such as don't want to give control to subscriber to change events.
If we want to validate if a particular event is subscribed in the subscriber class, better publisher will set a flag in its class when event is subscriber and clear the flag when it is unsubscriber.
If subscriber can access the flag of publisher, very easily identifiable whether the particular event is subscriber or not by checking the flag value.

How can I pass an event to a function in C#?

I am looking to pass an event to a helper function. This function will attach a method to the event. However, I am having trouble properly passing the event. I have tried passing a EventHandler<TEventArgs>. It compiles, but events are not attached (but are still added; it seems a copy of the event handler is made).
For example, if I have this:
public event EventHandler<EventArgs> MyEvent;
And the helper function:
public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo)
{
eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); };
}
And the caller:
MyHelperFunction(MyEvent);
MyEvent(null, new EventArgs()); // Does nothing.
The reason why this does not work is += when applied to a delegate creates a new delegate which is the combination of the old and the new. It does not modify the existing delegate.
In order to get this to work you will have to pass the delegate by reference.
public static void Helper(ref EventHandler<EventArgs> e)
{
e+= (x,y) => {};
}
The reason this works outside of the method is because the LHS is still the actual field. So += will create a new delegate and assign back to the member field.
Just came up with this little helper. If it is your self-created Event you could use a wrapper like this. You can use your += operators to attach handlers as normal but can pass the wrapper around and even raise the event from elsewhere.
public class GenericEvent<T> where T:EventArgs
{
public event EventHandler<T> Source = delegate { };
public void Raise(object sender, T arg = default(T))
{
Source(sender, arg);
}
public void Raise(T arg = default(T))
{
Source(this, arg);
}
public void AddHandler(EventHandler<T> handler)
{
Source += handler;
}
public void RemoveHandler(EventHandler<T> handler)
{
Source -= handler;
}
public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler)
{
genericEvent.AddHandler(handler);
return genericEvent;
}
}
Create the event like:
public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>();
Attach handlers:
MyEvent += (s,e) => {};
Raise event:
MyEvent.Raise();
Just guessing: Have you tried passing it as ref?
public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo)
MyHelperFunction(ref MyEvent);
It's not exactly nice, but you can use reflection to do this.
public EventMonitor(object eventObject, string eventName)
{
_eventObject = eventObject;
_waitEvent = eventObject.GetType().GetEvent(eventName);
_handler = new EventHandler(SetEvent);
_waitEvent.AddEventHandler(eventObject, _handler);
}
Where eventObject is the object containing the event, and eventName is the name of the event.
SetEvent is your event handler.
I also have a dispose method like this:
public void Dispose()
{
_waitEvent.RemoveEventHandler(_eventObject, _handler);
}
I have a solution where I have an two interfaces. The first interface has methods for binding certain events, while the other interface has event methods that can be bound to those events.
The first interface's bind methods takes the second interface as parameter, which makes it possible to bind the events to the event methods of any class that implements the second interface.
Is that understandable, or would you prefer some code? :)
As many have pointed out, passing an event to a method is either not possible or not simple.
Please clarify, but I suspect your intended usage will look something like:
void Register()
{
var super = new SuperHandler();
//not valid syntax:
super.HandleEvent(MyEvent1);
super.HandleEvent(MyEvent2);
super.HandleEvent(MyEvent3);
super.HandleEvent(MyEvent4);
}
You can accomplish this simply by making your intended generic event handlers accessible publicly (or internally, if you desire):
public static class GenericHandler
{
public static void HandleAnyEvent(object sender, EventArgs e)
{
//handle
}
}
public class SomeClass
{
void RegisterEvents()
{
var r = new EventRaiser();
r.ImportantThingHappened += GenericHandler.HandleAnyEvent;
}
}
In this example my catch-all handler is in a static class, but you can just as well use a non-static class. Also, I see that in your example you have made the method generic (TEventArgs). Because all EventHandler derivatives (such as CancelEventHandler) match the base EventHandler, you do not need to involve generics (nor would it be helpful).
If the registration logic is complex or you must keep the EventHandler private, consider using Interface Events. This may not meet your intended goal of reducing the amount of code, but it will allow you to create a class that can predictably handle all of the events of a specific type.
interface IRaiseEvents
{
event EventHandler ConnectionCreated;
event EventHandler ConnectionLost;
}
public class SuperHandler
{
void RegisterEvents(IRaiseEvents raiser)
{
raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected.");
raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected.");
}
}
Pass something like Action e = e => myevent += e;
And call from method with the handler? It has the benefit of working with .NET classes.

How to ensure an event is only subscribed to once

I would like to ensure that I only subscribe once in a particular class for an event on an instance.
For example I would like to be able to do the following:
if (*not already subscribed*)
{
member.Event += new MemeberClass.Delegate(handler);
}
How would I go about implementing such a guard?
I'm adding this in all the duplicate questions, just for the record. This pattern worked for me:
myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;
Note that doing this every time you register your handler will ensure that your handler is registered only once.
If you are talking about an event on a class that you have access to the source for then you could place the guard in the event definition.
private bool _eventHasSubscribers = false;
private EventHandler<MyDelegateType> _myEvent;
public event EventHandler<MyDelegateType> MyEvent
{
add
{
if (_myEvent == null)
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
That would ensure that only one subscriber can subscribe to the event on this instance of the class that provides the event.
EDIT please see comments about why the above code is a bad idea and not thread safe.
If your problem is that a single instance of the client is subscribing more than once (and you need multiple subscribers) then the client code is going to need to handle that. So replace
not already subscribed
with a bool member of the client class that gets set when you subscribe for the event the first time.
Edit (after accepted): Based on the comment from #Glen T (the submitter of the question) the code for the accepted solution he went with is in the client class:
if (alreadySubscribedFlag)
{
member.Event += new MemeberClass.Delegate(handler);
}
Where alreadySubscribedFlag is a member variable in the client class that tracks first subscription to the specific event.
People looking at the first code snippet here, please take note of #Rune's comment - it is not a good idea to change the behavior of subscribing to an event in a non-obvious way.
EDIT 31/7/2009: Please see comments from #Sam Saffron. As I already stated and Sam agrees the first method presented here is not a sensible way to modify the behavior of the event subscription. The consumers of the class need to know about its internal implementation to understand its behavior. Not very nice.
#Sam Saffron also comments about thread safety. I'm assuming that he is referring to the possible race condition where two subscribers (close to) simultaneously attempt to subscribe and they may both end up subscribing. A lock could be used to improve this. If you are planning to change the way event subscription works then I advise that you read about how to make the subscription add/remove properties thread safe.
As others have shown, you can override the add/remove properties of the event. Alternatively, you may want to ditch the event and simply have the class take a delegate as an argument in its constructor (or some other method), and instead of firing the event, call the supplied delegate.
Events imply that anyone can subscribe to them, whereas a delegate is one method you can pass to the class. Will probably be less surprising to the user of your library then, if you only use events when you actually want the one-to-many semantics it usually offers.
You can use Postsharper to write one attribute just once and use it on normal Events. Reuse the code. Code sample is given below.
[Serializable]
public class PreventEventHookedTwiceAttribute: EventInterceptionAspect
{
private readonly object _lockObject = new object();
readonly List<Delegate> _delegates = new List<Delegate>();
public override void OnAddHandler(EventInterceptionArgs args)
{
lock(_lockObject)
{
if(!_delegates.Contains(args.Handler))
{
_delegates.Add(args.Handler);
args.ProceedAddHandler();
}
}
}
public override void OnRemoveHandler(EventInterceptionArgs args)
{
lock(_lockObject)
{
if(_delegates.Contains(args.Handler))
{
_delegates.Remove(args.Handler);
args.ProceedRemoveHandler();
}
}
}
}
Just use it like this.
[PreventEventHookedTwice]
public static event Action<string> GoodEvent;
For details, look at Implement Postsharp EventInterceptionAspect to prevent an event Handler hooked twice
You would either need to store a separate flag indicating whether or not you'd subscribed or, if you have control over MemberClass, provide implementations of the add and remove methods for the event:
class MemberClass
{
private EventHandler _event;
public event EventHandler Event
{
add
{
if( /* handler not already added */ )
{
_event+= value;
}
}
remove
{
_event-= value;
}
}
}
To decide whether or not the handler has been added you'll need to compare the Delegates returned from GetInvocationList() on both _event and value.
I know this is an old Question, but the current Answers didn't work for me.
Looking at C# pattern to prevent an event handler hooked twice (labelled as a duplicate of this question), gives Answers that are closer, but still didn't work, possibly because of multi-threading causing the new event object to be different or maybe because I was using a custom event class. I ended up with a similar solution to the accepted Answer to the above Question.
private EventHandler<bar> foo;
public event EventHandler<bar> Foo
{
add
{
if (foo == null ||
!foo.GetInvocationList().Select(il => il.Method).Contains(value.Method))
{
foo += value;
}
}
remove
{
if (foo != null)
{
EventHandler<bar> eventMethod = (EventHandler<bar>)foo .GetInvocationList().FirstOrDefault(il => il.Method == value.Method);
if (eventMethod != null)
{
foo -= eventMethod;
}
}
}
}
With this, you'll also have to fire your event with foo.Invoke(...) instead of Foo.Invoke(...). You'll also need to include System.Linq, if you aren't already using it.
This solution isn't exactly pretty, but it works.
I did this recently and I'll just drop it here so it stays:
private bool subscribed;
if(!subscribed)
{
myClass.MyEvent += MyHandler;
subscribed = true;
}
private void MyHandler()
{
// Do stuff
myClass.MyEvent -= MyHandler;
subscribed = false;
}
Invoke only distinct elements from GetInvocationList while raising:
using System.Linq;
....
public event HandlerType SomeEvent;
....
//Raising code
foreach (HandlerType d in (SomeEvent?.GetInvocationList().Distinct() ?? Enumerable.Empty<Delegate>()).ToArray())
d.Invoke(sender, arg);
Example unit test:
class CA
{
public CA()
{ }
public void Inc()
=> count++;
public int count;
}
[TestMethod]
public void TestDistinctDelegates()
{
var a = new CA();
Action d0 = () => a.Inc();
var d = d0;
d += () => a.Inc();
d += d0;
d.Invoke();
Assert.AreEqual(3, a.count);
var l = d.GetInvocationList();
Assert.AreEqual(3, l.Length);
var distinct = l.Distinct().ToArray();
Assert.AreEqual(2, distinct.Length);
foreach (Action di in distinct)
di.Invoke();
Assert.AreEqual(3 + distinct.Length, a.count);
}
[TestMethod]
public void TestDistinctDelegates2()
{
var a = new CA();
Action d = a.Inc;
d += a.Inc;
d.Invoke();
Assert.AreEqual(2, a.count);
var distinct = d.GetInvocationList().Distinct().ToArray();
Assert.AreEqual(1, distinct.Length);
foreach (Action di in distinct)
di.Invoke();
Assert.AreEqual(3, a.count);
}

Categories

Resources