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.
Related
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.
I need to attach an event handler to an object, and I placed this code on a button click event. However, I noticed that this will cause the same event to attach multiple times with each click.
Is there a way to run a piece of code on class creation? The class in question is a static class btw.
I can do something like:
if (bool == false)
{
attach event handler;
bool = true;
}
Just not sure if this is the right way to do it. Thanks.
There are static constructors, that are (in principle) only run once per class.
Something like this:
public static class MyStaticClass
{
public static int MyStaticProperty;
//no accessors required, as this is never explicitly invoked
static MyStaticClass() //no parameters either
{
MyStaticProperty = 100;
}
}
....
//writes: 100
Console.WriteLine(MyStaticClass.MyStaticProperty);
However, if a constructor won't do it, because you have some parameters that need to be set, or there are some prerequisite steps that need to be done, I would indeed recommend a private boolean check, as you have done.
You use a constructor - it will run on class creation.
Constructors are class methods that are executed when an object of a class or struct is created. They have the same name as the class or struct, and usually initialize the data members of the new object.
For static classes, use static constructors:
A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only. It is called automatically before the first instance is created or any static members are referenced.
try
if(Button1.Click == null)
Button1.Click += new System.EventHandler(this.myEventHandler);
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);
}
}
}
I am having a rough time implementing eventing in a recent project.
I have verified that structuremap is scanning properly assemble and adding EventHandlers
Scan(cfg =>
{
cfg.TheCallingAssembly();
cfg.IncludeNamespace("ABC.EventHandler");
cfg.ConnectImplementationsToTypesClosing(typeof(IHandle<>));
});
public class StructureMapEventDispatcher : IEventDispatcher
{
public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
{
foreach (var handler in ObjectFactory.GetAllInstances<IHandle<TEvent>>())
{
handler.Handle(eventToDispatch);
}
}
}
Before I used to fire Event from Domain. Somthing like Dispatcher.RaiseEvent(new [domainEvent class](x,y,z));
and the event will get fired up. I had to change the design where I am now collectiong events in a collection
_domainEvents = new Collection<IDomainEvent>();
and then raising it after I have saved the domain to Repository
public static void Raise(ICollection<IDomainEvent> domainEvents)
{
foreach (var domainEvent in domainEvents)
{
DomainEventDispatcher.Raise(domainEvent);
}
}
but now
ObjectFactory.GetAllInstances<IHandle<TEvent>>() returns 0 count of handlers
if I watch for
ObjectFactory.GetAllInstances<IHandle<DomainEventClass>>() it returns collection of handlers properly ( currently I have 2 and it shows 2 count)
... I am assuming this has something to do with events being raised as of type IDomainEvent instead of actual type and that is making it hard for structuremap to resolve it.
How can I solve this issue?
Regards,
The Mar
--
Edit 1:
I have conformed that struturemap container contains all event handlers scanned from the assembly.
Edit 2
I dont know how to make this question attract more attention. I am adding bounty for a solution to achieve the results desired. If the question is not clear, please ask.
Basically I want the ObjectFactory.GetAllInstances<IHandle<TEvent>>() to return handlers for TEvent where TEvent is of Type IDomainEvent. Events to be raised are stored in Collection of IDomainEvent and raised after the fact that Domain was saved (from service layer).
I am thinking there should be some way to make structuremap know that the event raised as IDomainEvent is actually of Type DomainEvent
var eventsToRaise= dealer.EventsToRaise();
Adding Information from Debug Window:
After the events have been raised in the dispatcher window
Edit 3:
Eventhough eventToRaise shows as "DealerName Changed" and "DealerCommunicationChanged"
typeof(TEvent) gives Type as Domain.IDomainEvent
I guesss if it is possible to get be able to cast to right type ( from whereever VS watch window is getting info) the problem could get resolved
----- Result---
Both approach worked. I put both approached to 2 other members in my team and we felt that solution without reflection to be selected as right answer.
Today we will be doing a test with changed implementation and see if there are any issues with this solution in the solution.
I have upvoted reflection based solution as it is also right answer.
As you say, the problem is that you're asking structure map for all instances of IHandle<IDomainEvent> and it has none of those, structuremap has handlers for concrete events. You'd need to construct the type using the actual type of the event and then ask for all handlers of that event:
Type genericHandler = typeof(IHandle<>);
Type[] typeArgs = { eventToDispatch.GetType() };
Type neededHandler = genericHandler.MakeGenericType(typeArgs);
var handlers = ObjectFactory.GetAllInstances(neededHandler);
the problem is that you end up with an IList of objects and you need to cast them to the correct handler type and it get's a bit tricky.... a possible solution is to use reflection to call the Handle() method:
var methodInfo = neededHandler.GetMethod("Handle");
object[] methodArgs = new object[] { eventToDispatch };
foreach (var h in handlers)
{
methodInfo.Invoke(h, methodArgs);
}
Instead of a reflection-based approach, I would recommend using a record to hold onto the type information for you. Something like this:
interface IEventRecord
{
void Dispatch(IEventDispatcher dispatcher);
}
public class EventRecord<TEvent> : IEventRecord where TEvent : IDomainEvent
{
TEvent theEvent;
public EventRecord(TEvent theEvent)
{
this.theEvent = theEvent;
}
public void Dispatch(IEventDispatcher dispatcher)
{
dispatcher.Dispatch(theEvent);
}
}
If you find instantiating an event records to be cumbersome, a helper could infer the type parameter like this:
public static EventRecord<TEvent> CreateEventRecord<TEvent>(TEvent theEvent) where TEvent : IDomainEvent
{
return new EventRecord<TEvent>(theEvent);
}
Which would allow you to instantiate event records like this:
var record = CreateEventRecord(myDomainEvent);
Then instead of holding onto a collection of IDomainEvents, hold onto a collection of IEventRecords that hold the necessary type data to raise themselves:
foreach (var eventRecord in Records)
{
eventRecord.Dispatch(myDispatcher);
}
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.