I am writing integration tests that involve FileSystemWatcher objects. To make things easier, I want to unsubscribe everything from an event delegate without having to hunt down every subscription. I already saw related post, Is it necessary to unsubscribe from events?. This is somewhat a duplicate, but I am specifically asking why this doesn't work with a FileSystemWatcher object.
It would be nice to do something like the following:
private void MethodName()
{
var watcher = new FileSystemWatcher(#"C:\Temp");
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.Changed = null; // A simple solution that smells of C++.
// A very C#-ish solution:
foreach (FileSystemEventHandler eventDelegate in
watcher.Changed.GetInvocationList())
watcher.Changed -= eventDelegate;
}
No matter how the Changed event is referenced, the compiler reports:
The event 'System.IO.FileSystemWatcher.Changed' can only appear on the left hand side of += or -=
The above code works just fine, when working with an event in the same class:
public event FileSystemEventHandler MyFileSystemEvent;
private void MethodName()
{
MyFileSystemEvent += new FileSystemEventHandler(watcher_Changed);
MyFileSystemEvent = null; // This works.
// This works, too.
foreach (FileSystemEventHandler eventDelegate in
MyFileSystemEvent.GetInvocationList())
watcher.Changed -= eventDelegate;
}
So, what am I missing? It seems that I should be able to do the same with the FileSystemWatcher events.
When you declare event in your class, it is an equivalent (almost) of the following code:
private FileSystemEventHandler _eventBackingField;
public event FileSystemEventHandler MyFileSystemEvent
{
add
{
_eventBackingField =
(FileSystemEventHandler)Delegate.Combine(_eventBackingField, value);
}
remove
{
_eventBackingField =
(FileSystemEventHandler)Delegate.Remove(_eventBackingField, value);
}
}
Notice that there is no set or get accessor for event (like for properties) and you can't explicitly write them.
When you write MyFileSystemEvent = null in your class, it is actually doing _eventBackingField = null, but outside your class there is no way to directly set this variable, you have only event add & remove accessors.
This might be a confusing behavior, because inside your class you can reference an event handler delegate by event name, and can't do that outside the class.
Short answer is += and -= are public operators while = is a private operator to the class that's declaring the event.
Related
I have this code:
private void loadGENIOFileToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlgFile = new OpenFileDialog();
dlgFile.InitialDirectory = Properties.Settings.Default.PreviousPath;
dlgFile.Title = "Select GENIO file";
dlgFile.Filter = "GENIO files (*.txt)|*.txt";
dlgFile.FilterIndex = 0;
dlgFile.Multiselect = false;
if (dlgFile.ShowDialog() == DialogResult.OK)
{
Properties.Settings.Default.PreviousPath = Path.GetDirectoryName(dlgFile.FileName);
DeleteView();
m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
m_oThreadServices.SetGenioFilePath(dlgFile.FileName);
m_oThreadServices.start();
}
}
But I am also trying to implement a MRU handler:
private void OnMruFile(int number, String filename)
{
if (File.Exists(filename))
{
Properties.Settings.Default.PreviousPath = Path.GetDirectoryName(filename);
DeleteView();
m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
mruMenu.SetFirstFile(number);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
m_oThreadServices.SetGenioFilePath(filename);
m_oThreadServices.start();
}
else
mruMenu.RemoveFile(number);
}
}
My m_oThreadServices.OnLoadingCompleted line of code seems to require that I use += and as a result, if I first load a file, it adds the first event handler. If I then go to use the MRU list to load a different file it ends up running two OnLoadingCompleted handlers.
I tried m_oThreadServices.OnLoadingCompleted = but it will not allow it. So what is the right way for me to intercept the event handler and not end up calling both sets of code? Am I going about it wrong?
Thank you.
You should make sure your event handlers are unsubscribed from the event source once the event is raised.
In order to do that, you have to modify a bit the anonymous handlers. For instance, this snippet:
m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
should be like this:
EventHandler onLoadingCompleted = null;
onLoadingCompleted = (_sender, _e) =>
{
m_oThreadServices.OnLoadingCompleted -= onLoadingCompleted;
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
m_oThreadServices.OnLoadingCompleted += onLoadingCompleted;
Same for the other.
The line
EventHandler onLoadingCompleted = null;
is needed to avoid using uninitialized variable compiler error here
m_oThreadServices.OnLoadingCompleted -= onLoadingCompleted;
You can remove a handler if it's a named function:
private void OnLoadingComplete_AddFile(_sender, _e)
{
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
}
...
m_oThreadServices.OnLoadingCompleted += OnLoadingComplete_AddFile;
...
m_oThreadServices.OnLoadingCompleted -= OnLoadingComplete_AddFile;
Removing a handler that hasn't been added (or has already been removed) is a no-op, so you can just remove the "other" handler before you add one: this will ensure there is at most one handler.
So basically += is syntactic sugar for calling Combine on your event. Delegates are stored in an Invocation List, and the default behavior when an event is fired is for each delegate in the invocation list to get called in the order they were added. This is why you cannot simply set OnLoadingCompleted to one delegate with an = sign - an event stores a list of delegates, not one.
You could remove a delegate with -= (syntactic sugar for calling Remove). Perhaps you want to formally declare the previous delegate somewhere rather than passing it as a lambda. This would let you remove it when you are done with it.
There is no straightforward way of removing anonymous or unknown events from a handler. However, you can take a look at this forum posting on MSDN: https://social.msdn.microsoft.com/Forums/vstudio/en-US/45071852-3a61-4181-9a25-068a8698b8b6/how-do-i-determine-if-an-event-has-a-handler-already?forum=netfxbcl
There is some code and discussion about using reflection to remove delegates from your event handler.
It might be better though to understand exactly what you are wanting to accomplish. Perhaps there is a better way to get the end-result that you are looking for rather than rewire events.
It isn't usually good practice to remove established event code to change the behavior of the code you want to implement. It can lead to unintended consequences, and erratic behavior. If event code is defined, it is almost always best to keep it in place and design your application around it.
On the other hand, if this is code that is added by you, or in your code-base, you can remove it, if you have done the proper research to validate its removal and not cause the application to break elsewhere. The best way to do that would be to have the event code in a named function:
public void MyEventCode(object sender, EventArgs args)
{
// Do event stuff..
}
Then you can remove the event by name:
control.DoMyEvent -= MyEventCode;
Delegate : I understand. But when I move to event, many things I don't understand so much. I read book, MSDN and some simple examples on Network, they both have same structures. For example, here is the link : Event Example
I take the first example, that the author said it's the most easiest example about C# Event.
Here is his code :
public class Metronome
{
public event TickHandler Tick;
public EventArgs e = null;
public delegate void TickHandler(Metronome m, EventArgs e);
public void Start()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
if (Tick != null)
{
Tick(this, e);
}
}
}
}
public class Listener
{
public void Subscribe(Metronome m)
{
m.Tick += new Metronome.TickHandler(HeardIt);
}
private void HeardIt(Metronome m, EventArgs e)
{
System.Console.WriteLine("HEARD IT");
}
}
class Test
{
static void Main()
{
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
You can notice line: public event TickHandler Tick. When I change to public TickHandler Tick, program still run the same. But new line I understand because it's just a pure delegate.
So, my question is : what is the real purpose of event keyword in line : public event TickHandler Tick. This is very important, because all examples always use like this, but I cannot explain why.
Thanks :)
Delegates and events are related concepts, but they are not the same thing. The term "delegate" tends to have two meanings (often glossed over):
A delegate type which is similar to a single method interface. (There are significant differences, but that's a reasonable starting point.)
An instance of that type, often created via a method group, such that when the delegate is "invoked", the method is called.
An event is neither of those. It's a kind of member in a type - a pair of add/remove methods, taking a delegate to subscribe to or unsubscribe from the event. The add and remove methods are used when you use foo.SomeEvent += handler; or foo.SomeEvent -= handler;.
This is very similar to how a property is really a pair of get/set methods (or possibly just one of the two).
When you declare a field-like event like this:
public event TickHandler Tick;
the compiler adds members to your class which are somewhat like this:
private TickHandler tick;
public event TickHandler
{
add { tick += value; }
remove { tick -= value; }
}
It's a bit more complicated than that, but that's the basic idea - it's a simple implementation of the event, just like an automatically implemented property. From inside the class, you can access the backing field, whereas outside the class you'll always end up just using the event.
Personally I think it's a pity that the declaration of a field-like event looks so much like a field of a delegate type - it leads to some of the misleading (IMO) statements found in some of the answers, as if the event keyword "modifies" a field declaration - when actually it means you're declaring something entirely different. I think it would have been clearer if field-like events looked more like automatically-implemented properties, e.g.
// Not real C#, but I wish it were...
public event TickHandler Tick { add; remove; }
I have a whole article going into rather more detail, which you may find useful.
The event keyword basically restricts the operation on your delegate.
You can no longer assign it manually using the = operator.
You can only add (using +=) or remove (using -=) delegates from your event, one by one. This is done in order to prevent some subscriber to "overwrite" other subscriptions.
Consequently, you cannot do: m.Tick = new Metronome.TickHandler(HeardIt)
"event" is a modifier. What's the benefit?
you can use events in interfaces
only the class declaring it can invoke an event
events expose an add and remove accessor that you can override and do custom stuff
events limit you to a specific signature of the assigned method SomeMethod(object source, EventArgs args) which provide you with additional information about the event.
You're correct - the addition of the event keyword seems to be almost redundant. However, there's a key difference between fields that are events and fields that are typed to a pure delegate. Using the event keyword means that objects external to the containing object can subscribe to the delegate, but they cannot invoke it. When you drop the event keyword, external objects can subscribe AND invoke the delegate (visibility permitting.)
When you add a listener to your program you add the event, not the delegate
see your code m.Tick +=
you see that part right there is you are asking for the property (type event) and you are adding to it a listener with the +=. Now you can only add to that Tick property a TickHandler type and if you override it you have to make your own that is the same format as TickHandler.
much like when you add to a string, or int.
string stringTest = string.Empty;
stringTest += "this works";
stringTest += 4; //this doesn't though
int intTest = 0;
intTest += 1; //works because the type is the same
intTest += "This doesn't work";
Metronome m = new Metronome();
Metronome.TickHandler myTicker = new Metronome.TickHandler(function);
m.Tick += myTicker; //works because it is the right type
m.Tick += 4; //doesn't work... wrong type
m.Tick += "This doesnt work either"; //string type is not TickHandler type
does that clear it up some?
As far as i'm informed an event is basically a multicast delegate, but with different access rules for the basic operations, that can be performed on delegates and events from within or outside the class they are defined in.
The operations are:
assign using the = operator
add/remove using the += and -= operator
invoke using the () operator
Operation | delegate | event
------------------+------------+--------
Inside class += / -= | valid | valid
------------------+------------+--------
Inside class = | valid | valid
------------------+------------+--------
Inside class () | valid | valid
------------------+------------+--------
Outside class += / -= | valid | valid
------------------+------------+--------
Outside class = | valid | not valid
------------------+------------+--------
Outside class () | valid | not valid
This gives you encapsulation which is always good OOP style. :-)
I think the main difference between using delegate and event is that the event can be only raised by the Server (means the author of the class)
If you remove the event keyword now you can raise the m.Tick(sender,e) in the Listener otherwise not.
public class Listener
{
public void Subscribe(Metronome m)
{
m.Tick += new Metronome.TickHandler(HeardIt);
}
private void RaisTick(object sender, EventArgs e)
{
m.Tick(sender,e);
}
private void HeardIt(Metronome m, EventArgs e)
{
System.Console.WriteLine("HEARD IT");
}
}
Consider the situation in which you want to subscribe to an event for one and only one notification. Once the first notification lands, you unsubscribe from all future events. Would the following pattern present any memory issues? It works, but I wasn't sure if the self-referencing closure could keeps things around in memory longer than desired.
public class Entity
{
public event EventHandler NotifyEvent;
}
// And then, elsewhere, for a listen-once handler, we might do this:
Entity entity = new Entity();
Action<object, EventArgs> listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= new EventHandler(listener);
};
entity.NotifyEvent += new EventHandler(listener);
Note that you have to declare 'listener' and assign a value (null). Otherwise the compiler complains about 'Use of unassigned local variable listener'
There is nothing wrong with this pattern. It's the very same pattern I and many others use for assigning and removing a lambda expression to an event handler.
While I think the general pattern is fine, I wouldn't go through Action<object, EventArgs>. I'd use:
EventHandler listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= listener;
};
entity.NotifyEvent += listener;
For Sample ....
SampleClass :
public class SampleClass
{
public delegate void BeforeEditorHandle();
public event BeforeEditorHandle OnBeforeEditor;
}
MainMethod
static void Main(string[] args)
{
SampleClass sc = new SampleClass();
// Add Event
sc.OnBeforeEditor +=new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
// Remove Event
sc.OnBeforeEditor -= new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
}
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
But I think this is very ugly when I have too much sources in the event....
Can anybody tell me the best way to remove the event please ?
You can assign the event handler/lambda to a variable which you can then subscribe and unsubscribe:
var myHandler = () => { };
sc.OnBeforeEditor += myHandler;
sc.OnBeforeEditor -= myHandler;
I'm pretty sure your code here won't work:
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
This is because restating the lambda creates a new different lambda.
You need to store the old reference and use it to unsubscribe:
BeforeEditorHandle myHandler=() => { }
sc.OnBeforeEditor += myHandler;
...
sc.OnBeforeEditor -= myHandler;
For easier unsubscribing you can collect your event handlers in a collection (For example List<BeforeEditorHandle>).
From MSDN:
It is important to notice that you
cannot easily unsubscribe from an
event if you used an anonymous
function to subscribe to it. To
unsubscribe in this scenario, it is
necessary to go back to the code where
you subscribe to the event, store the
anonymous method in a delegate
variable, and then add the delegate to
the event. In general, we recommend
that you do not use anonymous
functions to subscribe to events if
you will have to unsubscribe from the
event at some later point in your
code. For more information about
anonymous functions, see Anonymous
Functions (C# Programming Guide).
I have the following code where SprintServiceClient is a reference to a WCF Service-
public class OnlineService
{
private SprintServiceClient _client;
public OnlineService()
{
_client = new SprintServiceClient();
}
public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
{
_client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
_client.AddMemberToTeamAsync(user.ToUser(), projectId);
}
}
the problem is that every time AddMemberToTeam is called it adds another callback to client.AddMemberToTeamCompleted
i.e the first time AddMemberToTeam is called the callback is called once, the second time AddMemberToTeam is called the callback is called twice ect.
Is there any way to remove the eventhandler from AddMemberToTeamCompleted once the eventhandler has been called or use another method which takes in the callback?
You can refer to your anonymous method from inside itself as long as you assign a delegate to a variable first:
EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
{
_client.AddMemberToTeamCompleted -= handler;
callback(e.Result);
};
_client.AddMemberToTeamCompleted += handler;
Note that you need to declare the variable and assign it separately or the compiler will deem it uninitialized when you come to use it inside the method body.
The trick to making a self-unsubscribing event-handler is to capture the handler itself so you can use it in a -=. There is a problem of declaration and definite assignment, though; so we can't do something like:
EventHandler handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler; // <===== not yet defined
};
So instead we initialize to null first, so the declaration is before the usage, and it has a known value (null) before first used:
EventHandler handler = null;
handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler;
};
_client.AddMemberToTeamCompleted += handler;
No there is no way,
Apparantly Tim and Marc have another nice solution
But you can always just name them, and do the -= on the named eventhandler on this method ;)
Guessing your event:
_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;
and
public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
_client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
callback(e.Result)
}
Next problem is getting this callback in your listener. Perhaps putting it on a Property in the EventArgs (but that feels kinda dirty, I agree)