Adding one event handler to another - c#

I have a class which wraps another class and exposes several events from the class it's wrapping. (The instance it wraps can change)
I used the following code:
public event EventHandler AnEvent;
public OtherClass Inner {
get { /* ... */ }
set {
//...
if(value != null)
value.AnEvent += AnEvent;
//...
}
}
However, the events were raised inconsistently.
What's wrong with this code?

The problem is that Delegates are immutable.
If you add a handler to an event, it creates a new Delegate instance which contains the old handlers and the newly added handler. The old Delegate is not modified and is discarded.
When I write, value.AnEvent += AnEvent, it adds the Delegate containing the current handlers (if any) to the inner class's event. However, changes to the outer class's event are ignored because they don't change the Delegate instance that I added to the inner classes event. Similarly, if I remove a handler after setting the Inner property, the handler isn't removed from the inner class's event.
There are two correct ways to do this.
I can make my own handler that invokes the wrapper's event, like this:
public event EventHandler AnEvent;
public OtherClass Inner {
get { /* ... */ }
set {
if(Inner != null)
Inner.AnEvent -= Inner_AnEvent;
//...
if(value != null)
value.AnEvent += Inner_AnEvent;
//...
}
}
void Inner_AnEvent(object sender, EventArgs e) {
var handler = AnEvent;
if (handler != null) handler(sender, e);
}
The other way is to make a custom event in the wrapper that adds its handlers to the inner class's event, like this:
EventHandler anEventDelegates
public OtherClass Inner {
get { /* ... */ }
set {
//...
if(value != null)
value.AnEvent += anEventDelegates;
//...
}
}
public event EventHandler AnEvent {
add {
anEventDelegates += value;
if (Inner != null) Inner.AnEvent += value;
}
remove {
anEventDelegates -= value;
if(Inner != null) Inner -= value;
}
}
Note that this is not entirely thread-safe.
I solved this problem myself and am posting the question & answer for the benefit of people with similar problems.

The your answer - there are two problems here...
First: in both cases, you are raising the outer event with the wrong sender. Someone subscribing to an event on the outer class would expect those classes to be raised with a sender of that outer class.
This is particularly important in things like winform Controls, or binding-list implementations, where the sender is used to identify the object between many that share a handler.
This should instead be something like:
void Inner_AnEvent(object sender, EventArgs e) {
var handler = AnEvent;
if (handler != null) handler(this, e);
}
The second (much more minor) issue is that you are currently taking out an event on the inner class even if the outer class has no subscribers. You can fix this with a bit more custom handling...
private EventHandler anEvent;
public event EventHandler AnEvent {
add { // note: not synchronized
bool first = anEvent == null;
anEvent += value;
if(first && anEvent != null && inner != null) {
inner.SomeEvent += Inner_AnEvent;
}
}
remove { // note: not synchronized
bool hadValue = anEvent != null;
anEvent -= value;
if(hadValue && anEvent == null && inner != null) {
inner.SomeEvent -= Inner_AnEvent;
}
}
}
(and similar code in the Inner get/set to only subscribe if we have listeners...
if(value != null && anEvent != null)
value.AnEvent += Inner_AnEvent;
This might be a big saver if you have lots of instances of the outer-class, but rarely use the event.

Related

Event handling - Visual Studio

I am having issue as described below. I am new to .NET/Visual Studio (2013) and I am trying to figure out why code below wont work.
I have following class
public class PropertySettings
{
...
// get single instance of this class
public static PropertySettings Instance
{
get { return thisInstance; }
}
// event declaration
public event EventHandler<MyObj> PropertyChanged;
...
public void SaveProperty(string propertyName, object obj)
{
var oldValue = obj.OldVal;
var newValue = obj.NewVal;
// Why is PropertyChanged event always null?
if (PropertyChanged != null && oldValue != newValue)
{
PropertyChanged(this, obj); // pass reference to itself
}
}
}
The SaveProperty method is checking that PropertyChanged != null and if so, it calls it by passing a reference to itself and obj.
Then the SaveProperty method is called from some other class like this:
PropertySettings.Instance.SaveProperty("Width", Width);
The problem I am having is that the PropertyChanged is always null so PropertyChanged event is never called.
If you have an instance of your class:
var x = new PropertySettings();
Then you need to "wire up" any event handlers like this:
// "wire up" AKA "subscribe to" AKA "register" event handler.
x.PropertyChanged += HandlePropertyChanged;
// e.g. event handler...
void HandlePropertyChanged(object sender, object e)
{
throw new NotImplementedException();
}
Otherwise, PropertyChanged == null will be true.

C# Inter class communication with extreme performance

Suppose I want to publish an event from the MarketDataProvider class. The problem is that the event logic resides 2 level deeper.MarketDataProvider --> Level1SocketClient --> Level1MessageHandler.
In short, I would like to raise the event from Level1MessageHandler that MarketDataProvider can publish. The performance here is critical because there are a lot of events generated. I would like to find a clean way of doing it without chaining events from each level.
Rather than having an Event itself, could the intermediate class just pass the Add and Remove calls on to the lowest level class? I.e.
public class Level1MessageHandler
{
public event EventHandler<MessageEventArgs> MessageReceived;
}
public class Level1SocketClient
{
Level1MessageHandler level1Handler;
public event EventHandler<MessageEventArgs> MessageReceived
{
add
{
level1Handler.MessageReceived += value;
}
remove
{
level1Handler.MessageReceived -= value;
}
}
}
This would at least cut out one level of delegate call.
(Or did I get the direction of invocation reversed? Anyway I think the idea is clear.)
Update
An interesting question arises: what happens if the intermediate listener needs to be disposable, and when disposed, remove all the events added through it? You can do it by recording the events added in a local event, like so:
public interface IMessagePublisher<TEventArgs> where TEventArgs : EventArgs
{
event EventHandler<TEventArgs> MessageReceived;
}
public class MessageRePublisher<TEventArgs> : IMessagePublisher<TEventArgs>, IDisposable where TEventArgs : EventArgs
{
readonly IMessagePublisher<TEventArgs> publisher;
public MessageRePublisher(IMessagePublisher<TEventArgs> publisher)
{
this.publisher = publisher;
}
EventHandler<TEventArgs> messageReceivedEventsAdded = null;
public event EventHandler<TEventArgs> MessageReceived
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
// events are multicast delegates, which are immutable. We need to remove the previous
// combined event, create a new combined event, then added that.
// More here: http://msdn.microsoft.com/en-us/magazine/cc163533.aspx
if (messageReceivedEventsAdded != null)
publisher.MessageReceived -= messageReceivedEventsAdded;
messageReceivedEventsAdded += value;
if (messageReceivedEventsAdded != null)
publisher.MessageReceived += messageReceivedEventsAdded;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
if (messageReceivedEventsAdded != null)
publisher.MessageReceived -= messageReceivedEventsAdded;
messageReceivedEventsAdded -= value;
if (messageReceivedEventsAdded != null)
publisher.MessageReceived += messageReceivedEventsAdded;
}
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (messageReceivedEventsAdded != null && publisher != null)
{
publisher.MessageReceived -= messageReceivedEventsAdded;
}
}
messageReceivedEventsAdded = null;
}
#endregion
}
The intermediate listener combines all its events into a single concatenated event, then adds and removes that every time.

c# check if event is null

I have this event in a webservice:
public event FindProductsByCharacteristicsCompletedEventHandler FindProductsByCharacteristicsCompleted
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_findProductsByCharacteristicsCompleted += value;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_findProductsByCharacteristicsCompleted -= value;
}
}
And im then checking if the event value is null with this later in the class:
private void OnFindProductsByCharacteristicsOperationCompleted(object arg)
{
var handler = _findProductsByCharacteristicsCompleted;
if (handler == null)
return;
handler(this, new FindProductsByCharacteristicsCompletedEventArgs(completedEventArgs.Results, completedEventArgs.Error, completedEventArgs.Cancelled, completedEventArgs.UserState));
}
Your event implementation looks like it is an endless recursion. You are using the property itself in its implementation.
Change it to this:
private FindProductsByCharacteristicsCompletedEventHandler
_findProductsByCharacteristicsCompleted;
public event FindProductsByCharacteristicsCompletedEventHandler
FindProductsByCharacteristicsCompleted
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_findProductsByCharacteristicsCompleted += value;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_findProductsByCharacteristicsCompleted -= value;
}
}
And now, implement your method like this:
var handler = _findProductsByCharacteristicsCompleted;
if(handler == null)
return;
handler(this, new FindProductsByCharacteristicsCompletedEventArgs(...));
This has the advantage that it is thread-safe.
Even if someone detached the last handler from the event after you checked for null but before you actually raised the event, you would not get an exception, because you are operating on the unchanged local variable.

Check if event has any listeners?

Is it possible to detect if event has any listeners? (I need to dispose my event provider object, if nobody needs it)
Assume the class is in a 3rd party library and it can't be modified:
public class Data
{
public event EventHandler OnSave;
//other members
}
In your program:
Data d = new Data();
d.OnSave += delegate { Console.WriteLine("event"); };
var handler = typeof(Data).GetField("OnSave", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(d) as Delegate;
if (handler == null)
{
//no subscribers
}
else
{
var subscribers = handler.GetInvocationList();
//now you have the subscribers
}
You can check if event is != null.
By the way, in C# you need this check each time you raise an event:
if (TheEvent != null) {
TheEvent(this, e);
}
and the reason is exactly to check if the event has any listener.
EDIT
Since you can't access TheEvent from outside the class, you could implement a method that does the check:
public class TheClass {
public bool HasEventListeners() {
return TheEvent != null;
}
}
void Main()
{
Console.WriteLine(ContainsOnSomethingEvent()); // false
OnSomething += (o,e) => {};
Console.WriteLine(ContainsOnSomethingEvent()); // true
}
EventHandler mOnSomething;
event EventHandler OnSomething {
add { mOnSomething = (EventHandler)EventHandler.Combine(mOnSomething, value); }
remove { mOnSomething = (EventHandler)EventHandler.Remove(mOnSomething, value); }
}
public bool ContainsOnSomethingEvent() {
return mOnSomething != null && mOnSomething.GetInvocationList().Length > 0;
}

Question about custom events

I'm making custom events for C# and sometimes it isn't working.
This is how I'm making the event happen:
private bool isDoorOpen;
public bool IsDoorOpen {
get { return isDoorOpen;}
private set { isDoorOpen = value; DoorsChangeState(this, null);}
}
And these are the event declarations:
//events
public delegate void ChangedEventHandler(Elevator sender, EventArgs e);
public event ChangedEventHandler PositionChanged;
public event ChangedEventHandler DirectionChanged;
public event ChangedEventHandler BreaksChangeState;
public event ChangedEventHandler DoorsChangeState;
This works as long as there are methods attached to the events, but if there isn't, it throws a null ref exception. What am I doing wrong?
The recommended way to call an event is
var handler = this.DoorsChangeState;
if (handler != null)
handler(this, null);
The reason for copying the handler locally is incase the event handler changes on another thread while you're checking for null.
EDIT: Found the article talking about race conditions.
http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx
I know this question has been discussed (and answered) several times here on SO.
Also somewhere here i got the following extension methods to make this pattern more easy to use:
public static class EventHandlerExtensions
{
public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
var temp = handler;
if (temp != null)
{
temp(sender, args);
}
}
public static void FireEvent(this EventHandler handler, object sender)
{
var temp = handler;
if (temp != null)
{
temp(sender, EventArgs.Empty);
}
}
}
So in your code you can say:
public bool IsDoorOpen
{
get { return isDoorOpen;}
private set
{
isDoorOpen = value;
DoorsChangeState.FireEvent(this);
}
}
If a event isn't subscribed to when it fires, a NullReferenceException will be thrown. This is correct behaviour, not something you've done wrong.
You should check:
if(DoorsChangeState != null)
{
DoorsChangeState(this, null); // Only fire if subscribed to
}
Before invoking an event you must check if the event is null:
if (DoorsChangeState != null)
DoorsChangeState(this, null);
When DoorsChangeState is null that means there are no listeners on that event.
You need to check to see if the event has been subscribed to.
I use this standard form for throwing all of my events.
var temp = EventName;
if(EventName!= null)
temp(this, null);

Categories

Resources