Related
I have Windows Forms application with one main form (derived from base Form). Other modal forms that could be opened there are derived from my class ManagedForm, which is also derived from Form.
Also I have a static notifier service which fires some events like this:
public static class NotifierService
{
public delegate void NotifierServiceEventHandler(object sender, NotifierServiceEventArgs e);
private static readonly object Locker = new object();
private static NotifierServiceEventHandler _notifierServiceEventHandler;
#region Events
public static event NotifierServiceEventHandler OnOk
{
add
{
lock (Locker)
{
_notifierServiceEventHandler += value;
if (
_notifierServiceEventHandler.GetInvocationList()
.Count(
_ =>
_.Method.DeclaringType != null &&
value.Method.DeclaringType != null &&
_.Method.DeclaringType == value.Method.DeclaringType) <= 1)
return;
_notifierServiceEventHandler -= value;
}
}
remove
{
lock (Locker)
{
_notifierServiceEventHandler -= value;
}
}
}
// and many more events similar to previous...
#endregion
#region Event firing methods
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
NotifierServiceEventHandler handler;
lock (Locker)
{
handler = _notifierServiceEventHandler;
}
if (handler == null) return;
handler(typeof (NotifierService),
new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
#endregion
}
So in some places of code these events could be fired like:
NotifierService.NotifyExclamation("Fail!");
In the main form there is StatusStrip control used for notification purposes, and due to main form has subscribtion to these events -- their messages will be shown in the status strip.
BUT!, as I've said earlier, user may open other forms, and these forms could produce others and so on... (they are derived from one class ManagedForm which will be subscribed to NotifierService as soon as it has been created).
In these forms there is another logic how to notify user -- they need to show MessageBoxes with messages. As you can see, I've added some magic in event accessors to allow only one subscriber of any type, because w/o this all opened forms will generate their own MessageBoxes. But when one child ManagedForm has produced another and the second has been closed -- no MessageBoxes will be shown.
What magic should I implement to allow subscription from only first ManagedForm? Many thanks for any ideas.
EDIT: Suggested ideas doesn't solve this issue. I've tried to change event to this:
private static readonly object Locker = new object();
private static EventHandler<NotifierServiceEventArgs> _myEvent;
public static event EventHandler<NotifierServiceEventArgs> OnOk
{
add
{
if (_myEvent == null || _myEvent.GetInvocationList().All(_ => _.Method.DeclaringType != value.Method.DeclaringType))
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
Then I've open one modal child form and create a situation in which event has been fired by NotifierService. One MessageBox has been generated and shown (that's OK). Afterwards I've opened another modal form from first and create another situation in which another event has been fired. One MessageBox has been generated and shown (that's also OK). Now I'm closing second form and making a situation needed to fire event. No MessageBoxes has been shown (but in the status strip of the main form message of event has been shown correctly, so nothing has been changed from my first implementation).
Should I change something in remove clause? I do not need that only one subscriber should be, I need that each of the subscribers should be of distinct types. Sorry If bad English.
The way you are trying to solve the problem is fundamentally wrong by design. Your service class defines an event that will be fired under some circumstances. Some clients subscribe to that event, this way requesting to be notified when it happened. This is simply the .NET way of implementing the Observer pattern, so your service (being the subject or observable), should not apply any logic neither at subscribe nor the notify part, thus defeating the whole purpose of the pattern. Hans Passant already pointed to some flaws in your design, but even his solution is not perfect because looking at the event signature, it's totally unclear that only form instance methods are supposed to be registered - one can try using static method, anonymous lambda/method, some class method etc.
So, IMO the following are some of the viable choices you have.
(A) Keep your NotificationService events, but remove any "magic" from both subscribe and notify parts (shortly, use the regular way of defining and firing an event) and put the logic needed in your subscribers:
public static class NotifierService
{
public delegate void NotifierServiceEventHandler(object sender, NotifierServiceEventArgs e);
public static event NotifierServiceEventHandler OnOk;
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
var handler = OnOk;
if (handler != null)
handler(typeof(NotifierService), new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
}
Assuming that only the active form is supposed to handle the notifications, the existing handlers in both your MainForm and ManagedForm would use something like this inside their method body
if (this != ActiveForm) return;
// do the processing
You can even create a base form like this
class NotifiedForm : Form
{
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
NotifierService.OnOk += OnNotifyOK;
// similar for other events
}
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
NotifierService.OnOk -= OnNotifyOK;
// similar for other events
}
protected virtual void OnNotifyOK(object sender, NotifierServiceEventArgs e) { }
// similar for other events
}
and let your MainForm, ManagedForm (and any other is needed) inherit from that and just override the OnNotifyXXX methods and apply their logic.
To conclude, this approach would keep your service abstract and will leave the decisions to the clients of the service.
(B) If the sole purpose of your service is to act like a notification coordinator specifically for your forms, then you can remove events along with subscribe/unsubscribe parts (since Application.OpenForms and Form.ActiveForm already provide enough information needed) and handle the logic in your service. In order to do that, you'll need some sort of a base interface(s) or forms, and the easiest would be to use a similar approach to what was optional in the option (A) by creating a base form class like this
class NotifiedForm : Form
{
public virtual void OnNotifyOK(object sender, NotifierServiceEventArgs e) { }
// similar for other notifications
}
and let your MainForm, ManagedForm and other needed inherit from it. Note that there is no logic here (checking ActiveForm etc.) because now that's the responsibility of the caller. Then the service could be something like this:
public static class NotifierService
{
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
var target = Form.ActiveForm as NotifiedForm;
if (target != null)
target.OnNotifyOK(typeof(NotifierService), new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
// similar for other notifications
}
if the logic is to notify only the active form.
Or
public static class NotifierService
{
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
// Could also be a forward for, forach etc.
for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
{
var target = Application.OpenForms[i] as NotifiedForm;
if (target != null /* && someOtherCritaria(target) */)
{
target.OnNotifyOK(typeof(NotifierService), new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
// Could also continue
break;
}
}
}
// similar for other notifications
}
if some other logic is needed (which I doubt).
Hope that helps. In any case, option (A) is more flexible and allows much more usage scenarios, but if the usage scenarios are fixed by design, then the option (B) is better because it requires less from the clients (thus being less error prone) and provides a centralized application logic in one place.
I would like you proceed as follows:
Remove the magic from event accessor method and let all the subscribers subscribe to the event. So now you will have your main form and all other forms subscribed to the event.
Now place the magic in your event invocation method. For example in your NotifyOK method, first get the invocation list of deligate, now invoke each deligate one by one using DynamicInvoke or Invoke method of each deligate in the invocation list only if you have not already invoked for the particular DeclaringType. See the algo below:
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
NotifierServiceEventHandler handler;
lock (Locker)
{
handler = _notifierServiceEventHandler;
}
if (handler == null) return;
// Get invocation list of handler as you have done in event accessor
//initialise a new List<T> to hold the declaring types
// loop through each member (delegate) of invocation list
// if the current member declaration type is not in List<t>
// Invoke or DynamicInvoke current delegate
// add the declaration type of current delegate to List<t>
}
Try this:?)
private bool _eventHasSubscribers = false;
private EventHandler<MyDelegateType> _myEvent;
public event EventHandler<MyDelegateType> MyEvent
{
add
{
if (_myEvent == null)
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
i have reduced NotifierService to this:
public static class NotifierService
{
public static event EventHandler<NotifierServiceEventArgs> OnOk = delegate { };
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
OnOk(typeof(NotifierService),
new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
}
and then in ManagedForm used this handler
NotifierService.OnOk += Notify;
private void Notify(object sender, NotifierServiceEventArgs e)
{
// handle event in first open ManagedForm
if (Application.OpenForms.OfType<ManagedForm>().FirstOrDefault() == this)
{
// notification logic
}
}
if forms are opened as Modal (using ShowDialog()), it is possible to use another variant (according to this question):
private void Notify(object sender, NotifierServiceEventArgs e)
{
// handle event in active (last shown) ManagedForm
if (this.CanFocus)
{
// notification logic
}
}
so the idea is that all ManagedForms receive event data and then decide should they do something or not
P.S.: unsubscribe handlers on Dispose
protected override void Dispose(bool disposing)
{
if (disposing)
{
NotifierService.OnOk -= Notify;
}
// default
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
I have made a setup similar to yours & I see the problem.
I'll give 2 working suggestion to fix the issue (you may choose as per the changes required) -
Quickest fix with minimal changes to your original code -
So this is what I understand from the problem situation - You hooked event NotifierService.OnOk to an event handler in class ManagedForm & also wrote code to unhook the event handler from event NotifierService.OnOk when the form closes.
I'm assuming that you wrote the code to unhook the event handler from event NotifierService.OnOk when the form closes
But what I'm not sure is that when do you hook event NotifierService.OnOk to its event handler in managed form. Thats critical & I guess thats the only problem in your setup.
I assume you have set it up at a place which happens only once in the lifetime of form - like constructor or Load Event handler. And thats how I could reproduce the problem.
As fix, Just move hooking the event NotifierService.OnOk to its event handler at a place which which is called everytime the form becomes active
like
something like this -
public partial class ManagedFrom : Form
{
// this is the fix. Everytime the form comes up. It tries to register itself.
//The existing magic will consider its request to register only when the other form is closed or if its the 1st of its type.
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
NotifierService.OnOk += NotifierService_OnOk;
}
No more change needed, your existing logic in the event will take care of rest.
I have written the reason as comment in code above.
A little Better way but needs more changes
I would like to relieve the event OnOk form all the additional (& magical) responsibilities, I change the event
public static event NotifierServiceEventHandler OnOk
{
add
{
lock (Locker) // I'm not removing the locks. May be the publisher works in a multithreaded business layer.
{
_notifierServiceEventHandler += value;
}
}
remove
{
lock (Locker)
{
_notifierServiceEventHandler -= value;
}
}
}
Instead the subscriber should know when to Start and when to stop the subscription.
Therefore I change ManagedFrom
public partial class ManagedFrom : Form
{
//start the subscription
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
NotifierService.OnOk += NotifierService_OnOk;
}
//stop the subscription
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
NotifierService.OnOk -= NotifierService_OnOk;
}
In both the suggestions, my intend is to just fix the issue without introducing any new pattern. But do let me know if thats needed.
Also do let me know if it was helpful or if you think I took any wrong assumption .
To sum up:
there are multiple sources of events;
there are multiple targets;
there are different types of events which have to be processed differently.
Idea to use static manager is ok (unless you have performance issues, then splitting into multiple different message queues is the option), but cheating with subscribing/unsubscribing feels so wrong.
Make a simple event
public enum MessageType { StatusText, MessageBox }
public NotifyEventArgs: EventArgs
{
public MessageType Type { get; }
public string Message { get; }
public NotifyEventArgs(MessageType type, string message)
{
Type = type;
Message = message;
}
}
public static NotifyManager
{
public event EventHandler<NotifyMessageArgs> Notify;
public static OnEventHandler(MessageType type, string message) =>
Notify?.Invoke(null, new NotifyEventArgs(type, message));
}
Each form has to subscribe to this event when shown and unsubscribe when hidden. Not sure which events are the best here (got used to much to WPF Loaded, Unloaded, but there is no such in winforms, try to use Shown or VisibilityChanged perhaps).
Each form will receive event, but only one has to process MessageBox type (it is safe for all of them to display StatusMessage). For this you need some mechanizm to decide whenever form is the one (used to display message boxes). E.g. it can be active form:
void NotifyManager_Event(object sender, NotifyEventArgs e)
{
if(e.Type == MessageType.MessageBox && this == Form.ActiveForm)
MessageBox.Show(this, e.Message);
else
statusBar.Text = e.Message;
}
Are you sure that it is the task of the NotifierService to make sure that only one Form will show the notification?
If you would describe the tasks of a NotifierService, you would describe what it does and "whenever the NotifierService has something to notify, it will notify everyone who said that it wanted to be notified about the notifications"
This would make your notifierservice less dependant of the current application where it is used. If you want a completely different application with for instance only two Forms, where you want both Forms to react on the notifications you could not use this notifierservice.
But in my Forms application only one form may react on the notifications
That is right: it is your Forms application that has this constraint, not the notifierservice. You make a Forms aplication that may use any kind of notifierservice, but whatever notifierservice is used, only one of the Forms in my application may show the notification.
This means that you should have some rule to know whether a form should show the notifications or not
For instance:
Only the current form may show the notifications
Only the top left form may show the notifications
Only the main form may show the notifications, except when the settings form is visible
So let's assume you have something to determine which Form or Forms may react on notifications. This changes upon something happening: a form becomes active, or a form closes, a form becomes invisible, whatever.
Make a Boolean property for a ManagedForm that holds whether it should show notifications:
class ManagedForm
{
public bool ShowNotifications {get; set;}
public void OnEventNotification(object sender, ...)
{
if (this.ShowNotifications)
{
// show the notification
}
}
Now someone has to know which form should show the notification. This someone should set property ShowNotification.
For instance if only the active ManagedForm should show the notifications then the ManagedForm can decide for itsels:
public OnFormActiveChanged(object sender, ...)
{
this.ShowNotifications = this.Form.IsActive;
}
If all red Forms should show the notifications:
public OnFormBackColorChanged(object sender, ...)
{
this.ShowNotifications = this.Form.BackColor == Color.Red;
}
If you have a lot of Forms, with only a few that show notifications, then a lot events OnShowNotification will be called for nothing, but since this is just a function call it won't be a problem unless you show 1000 forms or so, and I guess in that you have more serious problems.
Summerized
Decide the criterium on which a ManagedForm should show the notifications
Decide when a different form should show the notifications
Create an event handler for when the form changes, let the event handler set property ShowNotification
When the event to show the notification occurs, check the property.
Subscriptions are useful if you actually want these events to propagate to each form, but that doesn't seem like what you want to do. Given any action, your code is needing to show only one dialog box and update the status text of the main form.
Maybe you should consider using a singleton pattern, instead. By using a static event handler, this is essentially what you are already doing.
public class MainAppForm : Form
{
static MainAppForm mainAppForm;
public MainAppForm()
{
mainAppForm = this;
}
public static void NotifyOk(Form sender, string fullMessage = "Ok.", string shortMessage = null)
{
mainAppForm.NotifyOk(sender, fullMessage, shortMessage);
}
public void NotifyOk(Form sender, string fullMessage, string shortMessage)
{
this.statusStrip.Invoke(delegate {
this.statusStrip.Text = shortMessage;
});
}
}
Suppose I have a class say a viewmode class mvvm. Then there are some event handlers created for this vm. then it could be used by many others with different situation.
So if I have an instance of myvm, I want to detect if there is any event handler hooked up and want to release it for memory issue.
What's the generic way to do this out of myvm, for example, I may not have the source code of myvm?
Events are designed such that code outside the class that declared them cannot get access to the underlying delegate. For example, according to Section "10.8 Events" in the C# Language specification (emphasis mine):
In an operation of the form x += y or x -= y, when x is an event and
the reference takes place outside the type that contains the
declaration of x, the result of the operation has type void (as
opposed to having the type of x, with the value of x after the
assignment). This rule prohibits external code from indirectly
examining the underlying delegate of an event.
Therefore, finding out what is subscribed to the event outside the class may be, at best, a "work around".
If you have access to the source of the class containing the event and you want to keep track of delegates hooked up to an event, implement the add and remove keyword in the event definition and manually keep track of them in a Dictionary.
If I understand you correctly.
This class wraps the unknown myvm class which I use SocketAsyncEventArgs to illustrate, cos obviously we don't have the source code for SocketAsyncEventArgs class.
And I wrapped the Completed event of SocketAsyncEventArgs class. When that event is triggered, _instance_Completed will be fired, then _myvm event will be fired. So what we need to do is subscribe/unsubscribe _myvm event.
Then I leave an event for people to subscribe/unsubscribe _myvm event, as subscribing/unsubscribing, the delegates are stored into a List therefore you can clear the
by call the ClearEvents() method.
Hope it will help.
public class WrapperClass
{
private EventHandler<SocketAsyncEventArgs> _myEvent;
private SocketAsyncEventArgs _myvm;
private List<Delegate> delegates;
public WrapperClass()
{
delegates = new List<Delegate>();
}
public void SetInstance(SocketAsyncEventArgs myvm)
{
_myvm = myvm;
_myvm.Completed += new EventHandler<SocketAsyncEventArgs>(_instance_Completed);
}
private void _instance_Completed(object sender, SocketAsyncEventArgs e)
{
if (_myEvent != null)
{
_myEvent(sender, e);
}
}
public event EventHandler<SocketAsyncEventArgs> myEvent
{
add
{
delegates.Add(value);
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Combine(_myEvent, value);
}
remove
{
delegates.Remove(value);
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Remove(_myEvent, value);
}
}
public void ClearEvents()
{
foreach (var d in delegates)
{
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Remove(_myEvent, d);
}
}
}
I'm a beginner in C# and having hard times understanding Events in C# .. The book i read (Illustrated C# 2008) gives an example about it , and there are few thing i need to ask about , so i will past the code here and point out the things i don't understand .
public class MyTimerClass
{
public event EventHandler Elapsed;
private void OnOneSecond(object source, EventArgs args)
{
if (Elapsed != null)
Elapsed(source, args);
}
}
class ClassA
{
public void TimerHandlerA(object obj, EventArgs e) // Event handler
{
Console.WriteLine("Class A handler called");
}
}
class ClassB
{
public static void TimerHandlerB(object obj, EventArgs e) // Static
{
Console.WriteLine("Class B handler called");
}
}
class Program
{
static void Main( )
{
ClassA ca = new ClassA(); // Create the class object.
MyTimerClass mc = new MyTimerClass(); // Create the timer object.
mc.Elapsed += ca.TimerHandlerA; // Add handler A -- instance.
mc.Elapsed += ClassB.TimerHandlerB; // Add handler B -- static.
Thread.Sleep(2250);
}
}
Ok, now the line after declaring the event here public event EventHandler Elapsed;
which is private void OnOneSecond(object source, EventArgs args) i know that the two line after it is to check if the event contains methods or not , but what is OnOneSecound for ? or when it's called ? or what it's named .. it's not event handler i guess right ? and what's the relationship between Elapsed and OnOneSecond ?
sorry for the newbie question .. and thanks in advance :)
the OnOneSecond method will be called internally by the MyTimerClass when it needs to invoke the event.
This is a common pattern used by most controls, including the microsoft ones.
Basically you dont need to be checking if the event is set in multiple places, you just do it in this one method then call this method internally to raise the event.
I tend not to pass the event args to the OnXXX method though, for example.
public event EventHandler<EventArgs> SomeEvent;
protected virtual void OnSomeEvent()
{
if (this.SomeEvent !=null)
{
this.SomeEvent.Invoke(this,EventArgs.Empty);
}
}
then to raise it
this.OnSomeEvent();
This is the method, that you call to raise the event safely.
the problem is, you can basically call
Elapsed(source, args)
but if there is noone connected to the event, this will raise a Reference Null exception. as the event is null, when nobody hears on it.
a better solution is, that you directly add a subscriber to the events. then you can safely call it directly. as there will be allways a subscriber.
public event Action<EventArgs> Elapsed = val => { };
(note that with the = its directly assigned. val => { } is a Lambda expression, that defines a empty subscriber.)
Also, look into the Reactive Framework for .net
if you want to do a lot of event stuff, this is the correct solution for it.
That allows you to manually fire the events from thein the class.
That is the standard pattern for raising internal events that's why it is private.
OnOneSecond is just a helper method defined to raise the event. You can use events without such methods, it is just an established pattern to wrap the if (Elapsed != null) check in a method with a name that starts with On...
Technically you could just use Elapsed(source, args) instead of OnOneSecond(source, args), but this will throw NullReferenceException if there are no listeners registered.
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.
Is the implementation below thread-safe? If not what am I missing? Should I have the volatile keywords somewhere? Or a lock somewhere in the OnProcessingCompleted method? If so, where?
public abstract class ProcessBase : IProcess
{
private readonly object completedEventLock = new object();
private event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
event EventHandler<ProcessCompletedEventArgs> IProcess.ProcessCompleted
{
add
{
lock (completedEventLock)
ProcessCompleted += value;
}
remove
{
lock (completedEventLock)
ProcessCompleted -= value;
}
}
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler = ProcessCompleted;
if (handler != null)
handler(this, e);
}
}
Note: The reason why I have private event and explicit interface stuff, is because it is an abstract base class. And the classes that inherit from it shouldn't do anything with that event directly. Added the class wrapper so that it is more clear =)
You need to lock when you fetch the handler too, otherwise you may not have the latest value:
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler;
lock (completedEventLock)
{
handler = ProcessCompleted;
}
if (handler != null)
handler(this, e);
}
Note that this doesn't prevent a race condition where we've decided we're going to execute a set of handlers and then one handler unsubscribed. It will still be called, because we've fetched the multicast delegate containing it into the handler variable.
There's not a lot you can do about this, other than making the handler itself aware that it shouldn't be called any more.
It's arguably better to just not try to make the events thread-safe - specify that the subscription should only change in the thread which will raise the event.
There is no need for the private ProcessCompleted member to be an event - it could just be a field: private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - inside the class it always goes straight to the field, so the event stuff is lost anyway.
The approach you've shown with an explicit lock object isn't much more thread-safe than just having a field-like event (i.e. public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - the only difference is that you aren't locking "this" (which is a good thing - you should ideally avoid locking on this).. The "handler variable" approach is the right one, but there are still side-effects you should be aware of.