What's a proper way to unsibscribe from events in c#? - c#

I have a model class with an event which I subscribe from other classes. I want to subscribe and un-subscribe in each class correctly.
First I want to guarantee that in MyClass I unsibscribe only once even that code is in few methods.
Second there are other classes except MyClass witch use OnMyEvent so I don't want to unintentionally unsibscribe from the event in the class.
MyClass(IModel model)
{
_model = model;
_model.OnMyEvent +=EventHandle;
}
Close()
{
_model.OnMyEvent -=EventHandle;
}
Disconnect()
{
//I want to check if OnMyEvent has already unsibscribed
//Moreover OnMyEvent is used in other classes and
//I don't want to mess up with it here
_model.OnMyEvent -=EventHandle;
}

If you only subscribe once, it doesn't matter how many times you unsubscribe - unsubscribing when you don't have a subscription is a no-op. Equally, the entire point of the event API is that you can't accidentally unsubscribe other subscriptions (either other types, or other instances of the same type).
As such, the code as shown should be fine, although it might be worth moving the two calls to a single method that handles this. That might be overkill, though.
Also, if your type is IDisposable, make sure it gets called in that code-path too (presumably by calling Close()).

You can safely unsubscribe the same handler from an event multiple times. Additional checking is not required and would be contraproductive.

If you want to guarantee you only unsubscribe once, you can use the GetInvocationList method:
if (_model.OnMyEvent != null && _model.GetInvocationList().Contains(EventHandle))
{
_model.OnMyEvent -= EventHandle
}
But as mentioned by the others, you can unsubscribe multiple times. If this really isn't a problem, keep it that way. The solution I propose is just code-noise. Simply unsubscribing in one line is much neater, and easier to read when your class starts to grow.

You can also control subscriptions and unsubsriptions with this declaration. But you also have to iterate through dictionary and call manually subscribed delegates.
private Dictionary<string, EventHandler> TestEvents { get; }
public event EventHandler TestEvent
{
add
{
string name = value.GetType().FullName;
if (!TestEvents.ContainsKey(name))
{
TestEvents.Add(name, value);
}
}
remove
{
string name = value.GetType().FullName;
if (TestEvents.ContainsKey(name))
{
TestEvents.Remove(name);
}
}
}

Related

C# What alternative ways are there to propagate an Event down a call chain?

Imagine that Main.cs calls sub.cs which calls action.cs. action.cs raises and event which sub.cs subscribes to, however, sub.cs does not care about the event it is only main.cs that wants to know about this so sub.cs raises the event again so that main.cs can subscribe to it and discover that action.cs has raised the original event; which seems so cumbersome.
What alternatives are there to passing events on through a chain of method calls?
You can directly attach event exposed in Sub at event exposed in Action, of course event need to be exposed both in Sub and in Action:
class SubClass
{
public event EventHandler MyEvent
{
add
{
_action.MyEvent += value;
}
remove
{
_action.MyEvent -= value;
}
}
private ActionClass _action;
}
With this solution you still have to declare event twice but you do not chain method calls and you can omit event handler in SubClass.
There are alternatives, of course, but you may need to change your design and I don't have enough context to suggest anything. In general I'd start with simplest possible solution. If you need just to bubble one event this may be enough but if you need to expose many of them then you may consider to introduce a third object which exposes what you need and make accessible through SubClass from ActionClass, something like this (but please with better names):
public sealed class Notifications
{
public event EventHandler MyEvent;
internal void RaiseMyEvent(EventArgs e)
{
var myEvent = MyEvent;
if (myEvent != null)
myEvent(this, e);
}
}
class MyAction
{
public Notifications Notifications
{
get { return _notifications; }
}
// ...
}
class SubClass
{
public Notifications Notifications
{
get { return _action.Notifications; }
}
// ...
}
Note that this example is just a proof of concept.
You may want to use pub-sub with topics, for instance see https://www.rabbitmq.com/tutorials/tutorial-three-dotnet.html
(You don't need rabbitmq for that you can implement simple pub-sub yourself or take one from github/MSDN see: https://msdn.microsoft.com/en-us/library/ms752254(v=vs.110).aspx, https://github.com/upta/pubsub/blob/master/README.md)
You could use a callback instead of events.
You can add a callback function as additional parameter to the methods of method chain.
E.g. if the method is doSomething() replace it with doSomething(Action action) and Main.c calls this method with Sub.doSomething(() => ReactToTheEvent()); and Action.cs calls action(); insetad of raising the event.

Event handling raising InvalidOperationException - looking for advice

I am kind of new to programming in this manner - is there a way that I can work around or a recommended practice to using events and handlers?
eg:
class objectA
{
public List<Handler> handlers;
...
public onActionHappened
{
foreach(Handler h in handlers)
{
raiseEvent(this, eventArgs);
}
}
...
public void DeleteThis()
{
handlers = null
}
}
raiseEvent() will go on an call a few other methods, one of which will invoke DeleteThis(). When everything ends and the program flow returns back to raiseEvent() at the "}" for the foreach loop, it finds that handler has been modified = null, thus throwing the error of InvalidOperationException.
Some method handling should disable this objectA as part of the functionality - thus Deletethis() MAY be called by client code at some point. To fix this, I had modified from List handlers to just a single Handler object, but I feel that that should be a better way of workaround. Or better way of coding.
Any advice? Thanks in advance!
If you use ToArray on the list, you create a copy of its contents and are not dependant on the handler variable itself:
foreach(Handler h in handlers.ToArray()
{
//optional break if you don't want the loop to continue after DeleteThis is called: if(handlers==null)break;
raiseEvent(this, eventArgs);
}
To address the core of your question: The most straightforward way to fix the issue is to assign the list to a local variable before enumerating over it.
class objectA
{
public List<Handler> handlers;
...
public void OnActionHappened()
{
List<Handler> lh = handlers;
// TODO: Would probably make sense to check if lh is null here.
foreach(Handler h in lh)
{
h.raiseEvent(this, eventArgs);
}
}
...
public void DeleteThis()
{
handlers = null;
}
}
There is really no need to create a copy of the list as suggested elsewhere.
Since you seem to be new to C# programming, let me give you some idea what is going on here.
List<T> is a reference type. Let us assume that you create a new List<T> by calling its constructor:
List<Handler> handlers = new List<Handler>();
Now, executing this statement creates two things in the computer's memory:
The list object itself.
A variable ("handlers") that refers to the list object.
Now, if the computer executes the following line:
List<Handler> lh = handlers;
we end up with something like this:
Finally, if the computer executes the following line:
handlers = null;
the situation looks as follows:
As you can see, this way we maintain a valid reference to the list object via the local list variable "lh" and setting the member variable "handlers" to null doesn't affect the foreach enumeration any longer.
An event cannot be triggered outside the class in which the event is defined. So, if you move handlers outside class A, you can no more trigger events in handlers in the class A.
To work around this issue, put handlers in another class, say class B, and define a public method that triggers the events in the handlers in the class B (in this case, the onActionHappened method). For class A, simply call that public method (onActionHappened) of the class B.

Handling an event that might not have any subscriber

If a class fires events in its methods, the class does not have to know what or who subscribes its events. It is not also important if there is any subscriber.
In the code below, if there is not any subscriber to OnTrigger event, an exception occures.
public class EventTrigger
{
public static void Main(string[] args)
{
(new EventTrigger()).Trigger();
}
public delegate void Delegate1();
public event Delegate1 OnTrigger;
void Trigger()
{
OnTrigger();
}
}
I can call the event like this;
if (OnTrigger != null)
{
OnTrigger();
}
But it seems weird to me, because the triggerer does not have to know about subscription.
My question is:
Do I have to check if the event reference is null whenever I use it.
If you initialize OnTrigger then you wont have to do the check.
e.g.
public event Action OnTrigger = delegate { };
Yes ´delegate { }´ instantiates a new object, which is why this allows you to omit the ´null´ check.
´delegate { }´ returns nothing, so if you want it to return a string (which you need if Delegate1 returns a string) then you simply add ´return "";´ e.g.:
public event Action OnTrigger = delegate { return string.Empty; };
One I should add is that it's bad practice to do this in order to avoid a null check, as it's a lazy mans hack. Some code can still set the event to null, ´OnTrigger = null´ will break your code. And when it comes to (de)serialization it wont work at all.
The triggerer doesn't have to know about the individual subscribers, but it does need to know about subscription. You have to either do the null check every time or use the work-around Simon suggested.

Additional functionality Events provide over delegate

As I understand an Event is a way for a class to allow clients to give it delegates to methods that should be called when the event occurs. When the event occurs, the delegate(s) given to it by its clients are invoked.
But as demonstrated in following code above said functionality can also be achieved by delegate only i.e. without using delegate.
class Program
{
static void Main(string[] args)
{
ListWithChangedEvent lwce = new ListWithChangedEvent();
lwce.delegateVariable = DelegateTestMethod;
lwce.Add("test");
Console.ReadLine();
}
public static void DelegateTestMethod(object sender, object e)
{
}
}
public delegate void ChangedEventHandler(object sender, object e);
public class ListWithChangedEvent : System.Collections.ArrayList
{
public override int Add(object value)
{
int result = base.Add(value);
if (delegateVariable != null)
delegateVariable(this, "");
return result;
}
public ChangedEventHandler delegateVariable;
}
So, I was wondering what additional functionality does Events provide?
So, I was wondering what additional functionality does Events provide?
Events provide two distinctly different advantages over exposing a public delegate:
You're making the intent very clear. A delegate is typically exposed publically for a very different purpose than an "event" - by using an event, you're very clearly saying "this is something that will get raised at a specific point". Exposing a delegate typically has a different meaning - most often a delegate in a public API is a required input for that API - ie: something that is used directly by the method, not an optional notification mechanism triggered by the method.
Events, technically, are not necessarily just a delegate. An event actually has the option of allowing custom add and remove accessors, which allow you to manually determine what happens when a subscriber subscribes or unsubscribes from the event. For example, many implementations of ICommand.CanExecuteChanged actually don't include their own delegate at all - but silently route to the CommandManager's RequerySuggested event.
Your example allows for a single delegate to be called. The event is a collection of delegates, meaning you can += and -= your heart away (even during event invocation).
event is just the access approach to the handler.
it wont allow you to do myHandler=myFunc;
only using += ( from outer class)
it was made that if another dumb use your code - so he wont destroy your chain by using = so you allow him only += or -=

Prioritising Event Handlers

I have the following code where I am handling an event twice. However I always want to ensure that mynewclass always handles the event first and then the local event handler code fires. I understand the MyClass event should fire first as that is the one created first but because the thread and enqueuing is taking place, I think its taking too long and its doing something in myhandleeventlocal before I want it to do that. Any way I can wait for it to happen?
public MyMainClass
{
private MyMethod()
{
MyClass mynewclass = new MyClass();
mynewclass.myObject += MyHandler(myhandleventlocal);
mynewclass.loadedevent += EventHandler(loadedevent)
}
private void myhandleventlocal()
{
//do stuff
}
private void loadedevent()
{
//do some stuff
}
}
public MyClass
{
public MyObject myObject;
public event loadedevent;
public MyClass()
{
myObject = new MyObject();
myObject += MyHandler(myhandlevent);
}
private void myhandlevent(long value, string detail)
{
//Start a thread
//Enqueue value and detail
//On seperate thread dequeue value and process it
//Raise loadedevent event
}
}
UPDATE: I have updated my question and code to demonstrate the problem.
By default the event handlers are called in the order you add them, so if you always add the handlers in the order you want them to fire then it should work.
From Jon Skeet's article on events and delegates:
[...] extra delegates are both added to and removed from the end of the list [...]
Note: You can override the default behaviour of events by changing the add and remove operations on your event to specify some other behaviour. You can then keep your event handlers in a list that you manage yourself and handle the firing order based on whatever rules you like.
If you can't guarantee the order the event handlers will be added, just add the one for mynewclass and then in that code call the other code.
Since event handlers are called in the order you add them, based on the code I see in your question, you can't make mynewclass's handler be called first. The event handler that MyClass creates is always added first.
One solution would be to control priority for the event handlers. Instead of using the builtin event handler +=/-= operators, you would instead have methods for adding and removing events where you could specify ordering explicitly. That way, if a class knows it needs to handle the event first, it could ask for such. Be careful, though, because you could easily run into a situation where multiple classes are each insisting that they handle the event first.
Here is some quick and dirty code to get you started:
class MyClass {
private LinkedList<MyEventHandler> eventHandlers;
public enum Ordering { First, Last, ... };
public void AddHandler(MyEventHandler handler, Ordering order) {
switch(order) {
case Ordering.First:
eventHandlers.AddFirst(handler);
break;
// fill in other cases here...
}
}
public void RaiseEvent() {
// call handlers in order
foreach(MyEventHandler handler in eventHandlers)
eventHandler();
}
}
Referring to siride solution, you can also implement your handlers and decide the position that way. Like inverting the order (always add at the begin) or add some logic.

Categories

Resources