I am trying to create an event broker that supports loose coupling while still allowing developers to use the familiar += syntax and intellisense. I am struggling with how to generically call the delegates without using DynamicInvoke.
General Idea:
All events are defined in interfaces, each event takes a single
argument that derives from EventInfoBase.
public delegate void EventDelegate<in T>(T eventInfo) where T : EventInfoBase;
On the subscriber side, the client asks the Windsor container for an instance that implements an event interface. Windsor returns a proxy that intercepts all += (add_xxx) and -= (remove_xxx) calls. The type and delegate information is stored for future lookup and execution when events are fired. Currently the delegate is stored as Delegate but would prefer to store it as something like EventDelegate.
On the publisher side, a publisher registers with the event broker as a source for an event interface. The event broker, via reflection, subscribes to each event with a delegate of type EventDelegate<EventInfoBase>.
When an event is fired, the event broker looks up any appropriate delegates and executes them.
Problem:
Adding event handlers to the publisher with the EventInfoBase base class is a legal use of contravariance. But the event broker cannot store the client subscriptions as EventDelegate<EventInfoBase>. Eric Lippert explains why in this blog post.
Any ideas of how I could store the client subscriptions (delegates) so they can be called later without the use of DynamicInvoke?
Updated with additional details:
Subscribers ask the event broker for an event interface and then subscribe to events as needed.
// a simple event interface
public class EventOneArgs : EventInfoBase { }
public class EventTwoArgs : EventInfoBase { }
public interface ISomeEvents
{
event EventDelegate<EventOneArgs> EventOne;
event EventDelegate<EventTwoArgs> EventTwo;
}
// client gets the event broker and requests the interface
// to the client it looks like a typical object with intellisense available
IWindsorContainer cont = BuildContainer();
var eb = cont.Resolve<IEventBroker>();
var service = eb.Request<ISomeEvents>();
service.EventOne += new EventDelegate<EventOneArgs>(service_EventOne);
service.EventTwo += new EventDelegate<EventTwoArgs>(service_EventTwo);
Under the hood the event broker knows nothing about the event interface, it returns a proxy for that interface. All += calls are intercepted and the subscriptions added dictionary of delegates.
public T Request<T>(string name = null) where T : class
{
ProxyGenerator proxygenerator = new ProxyGenerator();
return proxygenerator.CreateInterfaceProxyWithoutTarget(typeof(T),
new EventSubscriptionInterceptor(this, name)) as T;
}
public void Intercept(IInvocation invocation)
{
if (invocation.Method.IsSpecialName)
{
if (invocation.Method.Name.Substring(0, s_SubscribePrefix.Length) == s_SubscribePrefix) // "add_"
{
// DeclaringType.FullName will be the interface type
// Combined with the Name - prefix, it will uniquely define the event in the interface
string uniqueName = invocation.Method.DeclaringType.FullName + "." + invocation.Method.Name.Substring(s_SubscribePrefix.Length);
var #delegate = invocation.Arguments[0] as Delegate;
SubscirptionMgr.Subscribe(uniqueName, #delegate);
return;
}
// ...
}
}
The delegate stored in the SubscriptionManager is of type EventDelegate<T>, where T is the derived type defined by the event.
Publisher:
The publisher registers as a source of an event interface. The goal was to eliminate the need to explicitly call the event broker and allow the typical C# syntax of EventName(args).
public class SomeEventsImpl : ISomeEvents
{
#region ISomeEvents Members
public event Ase.EventBroker.EventDelegate<EventOneArgs> EventOne;
public event Ase.EventBroker.EventDelegate<EventTwoArgs> EventTwo;
#endregion
public SomeEventsImpl(Ase.EventBroker.IEventBroker eventBroker)
{
// register as a source of events
eventBroker.RegisterSource<ISomeEvents, SomeEventsImpl>(this);
}
public void Fire_EventOne()
{
if (EventOne != null)
{
EventOne(new EventOneArgs());
}
}
}
The event broker uses reflection to subscribe to all the events (AddEventHandler) in the interface with a common handler. I have not tried combining handlers yet. I created a wrapper class in case I needed additional information available when the event is fired, like the type.
public void RegisterSource<T, U>(U instance)
where T : class
where U : class
{
T instanceAsEvents = instance as T;
string eventInterfaceName = typeof(T).FullName;
foreach (var eventInfo in instanceAsEvents.GetType().GetEvents())
{
var wrapper = new PublishedEventWrapper(this, eventInterfaceName + "." + eventInfo.Name);
eventInfo.AddEventHandler(instance, wrapper.EventHandler);
}
}
class PublishedEventWrapper
{
private IEventPublisher m_publisher = null;
private readonly EventDelegate<EventInfoBase> m_handler;
private void EventInfoBaseHandler(EventInfoBase args)
{
if (m_publisher != null)
{
m_publisher.Publish(this, args);
}
}
public PublishedEventWrapper(IEventPublisher publisher, string eventName)
{
m_publisher = publisher;
EventName = eventName;
m_handler = new EventDelegate<EventInfoBase>(EventInfoBaseHandler);
}
public string EventName { get; private set; }
public EventDelegate<EventInfoBase> EventHandler
{
get { return m_handler; }
}
}
The problem I have been struggling with lies in the Publish. The Publish method looks up the delegates for the event and needs to execute them. Because of the performance problems with DynamicInvoke I would like to cast the delegate to the correct EventDelegate<T> form and call it directly but have not figured out a way to do it.
I have certainly learned a lot trying to figure this out but am running out of time. I am probably missing something simple. I have tried wrapping the delegate in another one (dynamically generated) that essentially looks like:
private static void WrapDelegate(Delegate d, DerivedInfo args)
{
var t = d as EventDelegate<DerivedInfo>;
if (t != null)
{
t(args);
}
}
Any guidance would be appreciated.
Any ideas of how I could store the client subscriptions (delegates) so they can be called later without the use of DynamicInvoke?
You could use Dictionary<Type, Delegate>, and then cast appropriately:
public void Subscribe<T>(EventDelegate<T> handler) where T : EventInfoBase
{
Delegate existingHandlerPlain;
// We don't actually care about the return value here...
dictionary.TryGetValue(typeof(T), out existingHandlerPlain);
EventDelegate<T> existingHandler = (EventDelegate<T>) existingHandlerPlain;
EventDelegate<T> newHandler = existingHandler + handler;
dictionary[typeof(T)] = newHandler;
}
public void Publish<T>(EventInfo<T> info) where T : EventInfoBase
{
Delegate handlerPlain;
if (dictionary.TryGetValue(typeof(T), out handlerPlain))
{
EventDelegate<T> handler = (EventDelegate<T>) handlerPlain;
handler(info);
}
}
The casts should always be safe because you're managing the contents yourself.
You'll still potentially get problems with variance though, if you try to combine eventhandlers which are actually of different types. If that's a problem, you'll need to explicitly use a List<EventHandler<T>> instead of using the "normal" combining operations.
Solution to wrapping the delegate:
I did manage to find a way to wrap the generic delegates with a delegate of a known type. This allows for standard calling convention of SomeDelegate(args) to be used. This solution takes in a generic delegate defined by:
public delegate void EventDelegate<in T>(T eventInfo) where T : EventInfoBase;
The generic delegate is wrapped by a delegate with a known signature which can be called by the infrastructure code. I have not yet verified the performance of this approach. The cost of calling MethodInfo.Invoke is incurred with the subscription to an event. The cost of firing an event is the code in the DelegateWrapper.
private static EventDelegate<EventInfoBase> DelegateWrapper<T>(Delegate #delegate) where T : EventInfoBase
{
return (o =>
{
var t = #delegate as EventDelegate<T>;
var args = o as T;
if (t != null && o != null)
{
t(args);
}
}
);
}
private static readonly MethodInfo s_eventMethodInfo = typeof(EventSubscriptionInterceptor).GetMethod("DelegateWrapper", BindingFlags.NonPublic | BindingFlags.Static);
private EventDelegate<EventInfoBase> GenerateDelegate(Delegate d)
{
MethodInfo closedMethod = s_eventMethodInfo.MakeGenericMethod(d.Method.GetParameters()[0].ParameterType);
var newDel = closedMethod.Invoke(null, new object[] { d }) as EventDelegate<EventInfoBase>;
return newDel;
}
At least this gives me a working prototype of a loosely coupled event broker with ‘mostly’ C# syntax.
Related
How,does one should call an event declared by interface so that all the classes that has implemented that interface get notified??
For example in structure like this,
public delegate void myDel(int value);
interface IEventCaller{
event myDel myDelEventCall;
}
public Class One : IEventCaller {
public event myDel myDelEventCall;
}
public Class Two : IEventCaller {
public event myDel myDelEventCall;
}
I want both class One and Two to get notify and act as event gets called, I am feeling somewhere I am going wrong direction , is it possible to do?
Actually what you want doesn't involve events. Events would be used by an object implementing IEventCaller to notify some object holding a reference to that object of some change. To invoke something on the object implementing IEventCaller would just require a method, for example Hello();
First, you need code that informs all the objects that implement this interface. To make that possible, you somewhere need to store a list of instances that want to get notified.
One solution would be to create a class that manages that list. Let's say like this
private static List<IEventCaller> eventCallers = new List<IEventCaller>();
public static void AddEventCaller(IEventCaller c)
{
eventCallers.Add(c);
}
public static void RemoveEventCaller(IEventCaller c)
{
eventCallers.Remove(c);
}
public static IEventCaller[] EventCallers
{
get { return eventCallers.ToArray() }
}
Of course this code needs to be thread safe, etc. I'd put all this into a singleton to be globally available.
Then, all objects that implement IEventCallers need to register/unregister accordingly. Thus, I'd also have them Implement IDisposable so that in the constructor you can do
public EventCallable()
{
Singleton.Instance.AddEventCaller(this);
}
and in the Dispose method you can do this:
public void Dispose(bool disposing)
{
Singleton.Instance.RemoveEventCaller(this);
}
Now the code that should notify every instance could just do this:
public void NotifyAll()
{
foreach (IEventCaller caller in Singleton.Instance.EventCallers)
caller.Hello();
}
I think you might be looking at this the other one around.
With events, you want to have an object which is the publisher, which is responsible for publishing the event and saying "hey guys, something just occurred and you should know about it", and you have your subscribers, which are the guys who say "Yo dawg, let me know when that thing occurs, so i can act on it".
What you can do is have the object which is responsible for the event occurring implement your interface:
public class Publisher : IEventCaller
{
public event MyDel MyDeleteEvent;
public void OnDeleteOccured()
{
var myDeleteEvent = MyDeleteEvent;
if (myDeleteEvent != null)
{
MyDeleteEvent(1);
}
}
}
And then have your One and Two objects register to that event occurring, where they pass a method which signature matches the delegate type of MyDel:
public class SubscriberOne
{
public void OnSomethingOccured(int value)
{
Console.WriteLine(value);
}
}
public class SubscriberTwo
{
public void OnSomethingOccured(int value)
{
Console.WriteLine(value);
}
}
And the registration goes:
void Main()
{
var publisher = new Publisher();
var subscriberOne = new SubscriberOne();
var subscriberTwo = new SubscriberTwo();
publisher.MyDeleteEvent += subscriberOne.OnSomethingOccured;
publisher.MyDeleteEvent += subscriberTwo.OnSomethingOccured;
}
Imagine that I have have Class1 obj1 = new Class1(). Class1 has many different events. I want to know what event and when does it happened, but I don't want to create event handler for every event of this object. How can I do it?
Example output that I want to see:
13:04:29 Obj1.OnEvent1
13:04:31 Obj1.OnEvent2
13:04:32 Obj1.OnEvent3
13:04:35 Obj1.OnEvent2
......................
P.S.: I am using Visual Studio 2012 Ultimate.
You have to instrument your code using, for example, the Semantic Logging Application Block.
If you follow the established practice of having a protected virtual OnEvent method the fires the *Event* event, you'll find out the the actual event is the OnEvent method is in fact the real event and the *Event* .NET event is just a notification others can subscribe to. And you want to log events and not event subscriptions.
You can have something that subscribes to the events and log it, but you'll also soon find out that you'll want to log more than those events.
If the delegate types of your eventhandler are uniform you can iterate over the events obtained from the type and attach your logging logic to the event. I use a logger class with methods for Subscibe and Unsubscribe (not implemented here)
Logger
public class EventLogger
{
public void Subscribe<T>(T obj)
{
// get the type to iterate over the EventInfo's
var type = typeof (T);
foreach (var eve in type.GetEvents())
{
EventInfo info = eve;
// attach our logging logic
eve.AddEventHandler(obj,
new EventHandler((sender, args) =>
{
Console.WriteLine(
"{0} {1}.{2}",
DateTime.Now,
obj,
info.Name);
}));
}
}
public void Unsubscribe()
{
//todo unsubscribe
}
}
Demo Class
public class Class1
{
// public delegate void MyDelegate(string key, DateTime date, int value);
public event EventHandler Foo;
public event EventHandler Bar;
// public event MyDelegate Cancel;
public void Raise()
{
this.Bar(null, EventArgs.Empty);
// this.Cancel(null, DateTime.Now, 4);
}
}
Usage
var c = new Class1();
c.Bar += (sender, eventArgs) => { Console.WriteLine("bar"); };
// have our logger ready
var logger = new EventLogger();
// attach logging to a specific instance
logger.Subscribe(c);
// raise our events
c.Raise();
Notice that we only can handle delegates of the type EventHandler. (I commented the delegate MyDelegate because that would break this simple implementation). If you have events that use different delegates you either need to build a switch statement in the Subscribe method to handle each differen type or implement a method to generate a dynamic assembly that can handle the delegate as shown here.
I've got the following class:
public class Terminal : IDisposable
{
readonly List<IListener> _listeners;
public Terminal(IEnumerable<IListener> listeners)
{
_listeners = new List<IListener>(listeners);
}
public void Subscribe(ref Action<string> source)
{
source += Broadcast;
//Store the reference somehow?
}
void Broadcast(string message)
{
foreach (var listener in _listeners) listener.Listen(message);
}
public void Dispose()
{
//Unsubscribe from all the stored sources?
}
}
I've searched for a while and it appears that an argument passed with the ref keyword can't be stored. Trying to add the source argument to a list or to assign it to a field variable doesn't allow it to keep a reference to the actual delegate's original reference; so my questions are:
Is there a way to unsubscribe from all the sources without passing their references again?
If not, how can the class be changed in order to support it, but still maintain the subscription by passing a delegate through a method?
Is it possible to achieve it without using Reflection?
Is it possible to achieve it without wrapping the delegate/event in a class and then passing the class as a parameter for the subscription?
Thank you.
EDIT: It appears that without using a Wrapper or Reflection, there's no solution to the given problem. My intention was to make the class as much portable as possible, without having to wrap delegates in helper classes. Thanks everyone for the contributions.
Edit: Ok, that was a bad idea, so back to the basics:
I recommend creating a wrapper class over an Action:
class ActionWrapper
{
public Action<string> Action;
}
And restructuring your initial class to work with wrappers:
private ActionWrapper localSource;
public void Subscribe(ActionWrapper source)
{
source.Action += Broadcast;
localSource = source;
}
public void Dispose()
{
localSource.Action -= Broadcast;
}
Now you should get the desired results.
public class Terminal : IDisposable
{
List<IListener> _listeners;
List<Action<string>> _sources;
public Terminal(IEnumerable<IListener> listeners)
{
_listeners = new List<IListener>(listeners);
_sources = new List<Action<string>>();
}
public void Subscribe(ref Action<string> source)
{
_sources.Add( source );
source += Broadcast;
}
void Broadcast(string message)
{
foreach (var listener in _listeners) listener.Listen(message);
}
public void Dispose()
{
foreach ( var s in _sources ) s -= Broadcast;
}
}
I would suggest that the subscription method should return an implementation of a SubscriptionHelper class, which implements IDisposable. A simple implementation would be for SubscriptionHelper to hold a reference to the subscription list and a copy of the subscription delegate; the subscription list itself would be a List<SubscriptionHelper>, and the Dispose method for SubscriptionHelper would remove itself from the list. Note that if the same delegate gets subscribed multiple times, each subscription will return a different SubscriptionHelper; calling Dispose on a SubscriptionHelper will cancel the subscription for which it had been returned.
Such an approach would be much cleaner than the Delegate.Combine/Delegate.Remove method used by the normal .net pattern, whose semantics can get very strange if an attempt is made to subscribe and unsubscribe multi-target delegates.
EDIT:
Yep, my bad - delegates are immutable types, so adding a method to an invocation list will actually create a new delegate instance.
Which leads to an answer no to your question. To unsubscribe the delegate you need to remove your Broadcast method from the delegate's invocation list. This means creating a new delegate and assigning it to the original field or variable. But you cannot access the original once you're out of Subscribe method. Plus there can be other copies of that original field/variable that have your method on the invocation list. And there is no way for you to know about all of them and change there values.
I'd suggest to declare an interface with the event for your purpose. This will be quite flexible approach.
public interface IMessageSource
{
event Action<string> OnMessage;
}
public class MessageSource : IMessageSource
{
public event Action<string> OnMessage;
public void Send(string m)
{
if (OnMessage!= null) OnMessage(m);
}
}
public class Terminal : IDisposable
{
private IList<IMessageSource> sources = new List<IMessageSource>();
public void Subscribe(IMessageSource source)
{
source.OnMessage += Broadcast;
sources.Add(source);
}
void Broadcast(string message)
{
Console.WriteLine(message);
}
public void Dispose()
{
foreach (var s in sources) s.OnMessage -= Broadcast;
}
}
Original answer
Is there a particular reason why you pass source delegate as ref? You need this if you, for example, want to return a different delegate from the method.
Otherwise, the delegate is reference type, so you can subscribe to it without passing it as ref...
It's reasonably simple, but there are a few pitfalls. If you store a reference to the source objects, as most of the examples so far have proposed, the object won't be garbage collected. The best way to avoid this is to use an WeakReference, that will allow the GC to work properly.
So, all you have to do is this:
1) Add a list of sources to the class:
private readonly List<WeakReference> _sources = new List<WeakReference>();
2) Add the source to the list:
public void Subscribe(ref Action<string> source)
{
source += Broadcast;
//Store the reference
_sources.Add(new WeakReference(source));
}
3) And then just implement dispose:
public void Dispose()
{
foreach (var r in _sources)
{
var source = (Action<string>) r.Target;
if (source != null)
{
source -= Broadcast;
source = null;
}
}
_sources.Clear();
}
That said, there's also the question of why the Action must be passed as a ref. In the current code, there's no reason for that. Anyway, it doesn't affect the problem or the solution.
Perhaps, instead of trying to store a reference to the delegate, have what calls Subscribe use its reference to the object with the delegate to create actions for the subscription and unsubscription . Its an additional parameter, but its still straightforward.
public void Subscribe(Action<Action<string>> addHandler,Action<Action<string>> removeHandler)
{
//Prevent error for possibly being null in closure
Action<string> onEvent = delegate { };
//Broadcast when the event occurs, unlisten after (you could store onEvent and remove handler yourself)
onEvent = (s) => { Broadcast(s); removeHandler(onEvent); };
addHandler(onEvent);
}
And an example subscribing.
public event Action<string> CallOccured;
public void Program()
{
Subscribe(a => CallOccured += a, a => CallOccured -= a);
CallOccured("Hello");
}
I want to test that class A's RegisterEventHandlers() method registers one of its methods as an EventHandler for an event on class B. How can I do that? I'm using moq, if that matters.
I don't think there's a way to inspect the event handler delegate from outside the class (please correct me if I'm wrong).
It'd be nice if I could trigger the event and then assert that my callback was called, but if I mock the interface of the A class (and set up an expectation for the callback) then I lose the implementation of RegisterEventHandlers(), which is the method I'm testing in the first place.
Mocking the B class's event would be the best option, but I don't see what method I'd have to intercept to do this. Is there some way to set up a mock for an event, and intercept the += method call?
Is there a clean solution to this?
You can get the invocation list for an event outside the class declaring the event - but it involves reflection. Below is a code example showing how you can determine which methods (on target instance a) are added to the event b.TheEvent after a call to a.RegisterEventHandlers(). Paste the code below in a code file and add to a form or console project: Test test = new Test(); test.Run();
using System;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
public class A
{
B m_b = new B();
public void RegisterEventHandlers()
{
m_b.TheEvent += new EventHandler(Handler_TheEvent);
m_b.TheEvent += new EventHandler(AnotherHandler_TheEvent);
}
public A()
{
m_b.TheEvent += new EventHandler(InitialHandler_TheEvent);
}
void InitialHandler_TheEvent(object sender, EventArgs e)
{ }
void Handler_TheEvent(object sender, EventArgs e)
{ }
void AnotherHandler_TheEvent(object sender, EventArgs e)
{ }
}
public class B
{
public event EventHandler TheEvent;
//{
// //Note that if we declared TheEvent without the add/remove methods, the
// //following would still generated internally and the underlying member
// //(here m_theEvent) can be accessed via Reflection. The automatically
// //generated version has a private field with the same name as the event
// //(i.e. "TheEvent")
// add { m_theEvent += value; }
// remove { m_theEvent -= value; }
//}
//EventHandler m_theEvent; //"TheEvent" if we don't implement add/remove
//The following shows how the event can be invoked using the underlying multicast delegate.
//We use this knowledge when invoking via reflection (of course, normally we just write
//if (TheEvent != null) TheEvent(this, EventArgs.Empty)
public void ExampleInvokeTheEvent()
{
Delegate[] dels = TheEvent.GetInvocationList();
foreach (Delegate del in dels)
{
MethodInfo method = del.Method;
//This does the same as ThisEvent(this, EventArgs.Empty) for a single registered target
method.Invoke(this, new object[] { EventArgs.Empty });
}
}
}
public class Test
{
List<Delegate> FindRegisteredDelegates(A instanceRegisteringEvents, B instanceWithEventHandler, string sEventName)
{
A a = instanceRegisteringEvents;
B b = instanceWithEventHandler;
//Lets assume that we know that we are looking for a private instance field with name sEventName ("TheEvent"),
//i.e the event handler does not implement add/remove.
//(otherwise we would need more reflection to determine what we are looking for)
BindingFlags filter = BindingFlags.Instance | BindingFlags.NonPublic;
//Lets assume that TheEvent does not implement the add and remove methods, in which case
//the name of the relevant field is just the same as the event itself
string sName = sEventName; //("TheEvent")
FieldInfo fieldTheEvent = b.GetType().GetField(sName, filter);
//The field that we get has type EventHandler and can be invoked as in ExampleInvokeTheEvent
EventHandler eh = (EventHandler)fieldTheEvent.GetValue(b);
//If the event handler is null then nobody has registered with it yet (just return an empty list)
if (eh == null) return new List<Delegate>();
List<Delegate> dels = new List<Delegate>(eh.GetInvocationList());
//Only return those elements in the invokation list whose target is a.
return dels.FindAll(delegate(Delegate del) { return Object.ReferenceEquals(del.Target, a); });
}
public void Run()
{
A a = new A();
//We would need to check the set of delegates returned before we call this
//Lets assume we know how to find the all instances of B that A has registered with
//For know, lets assume there is just one in the field m_b of A.
FieldInfo fieldB = a.GetType().GetField("m_b", BindingFlags.Instance | BindingFlags.NonPublic);
B b = (B)fieldB.GetValue(a);
//Now we can find out how many times a.RegisterEventHandlers is registered with b
List<Delegate> delsBefore = FindRegisteredDelegates(a, b, "TheEvent");
a.RegisterEventHandlers();
List<Delegate> delsAfter = FindRegisteredDelegates(a, b, "TheEvent");
List<Delegate> delsAdded = new List<Delegate>();
foreach (Delegate delAfter in delsAfter)
{
bool inBefore = false;
foreach (Delegate delBefore in delsBefore)
{
if ((delBefore.Method == delAfter.Method)
&& (Object.ReferenceEquals(delBefore.Target, delAfter.Target)))
{
//NOTE: The check for Object.ReferenceEquals(delBefore.Target, delAfter.Target) above is not necessary
// here since we defined FindRegisteredDelegates to only return those for which .Taget == a)
inBefore = true;
break;
}
}
if (!inBefore) delsAdded.Add(delAfter);
}
Debug.WriteLine("Handlers added to b.TheEvent in a.RegisterEventHandlers:");
foreach (Delegate del in delsAdded)
{
Debug.WriteLine(del.Method.Name);
}
}
}
When mocking B, declare the EventHandler like this:
public class B : IB
{
public int EventsRegistered;
public event EventHandler Junk
{
add
{
this.EventsRegistered++;
}
remove
{
this.EventsRegistered--;
}
}
}
I'm not certain that moq allows this, but I'm sure you can create your own mock class.
You are correct that you cannot access the event delegates from outside the class, this is a limitation within the C# language.
The most straight-forward approach to test this, would be to mock class B and then raise it's event and then observe the side-effects of the event being raised. This is slightly different than what you're looking for but it demonstrates class's A behavior rather than its implementation (this is what your tests should strive to do).
In order for this to work, class B must be mockable and the event that it exposes must also be virtual. Moq can't intercept events if they're not declared as virtual. Alternatively, if B is an interface be sure that the event is declared there.
public interface IEventProvider
{
event EventHandler OnEvent;
}
public class Example
{
public Example(IEventProvider e)
{
e.OnEvent += PerformWork;
}
private void PerformWork(object sender, EventArgs e)
{
// perform work
// event has an impact on this class that can be observed
// from the outside. this is just an example...
VisibleSideEffect = true;
}
public bool VisibleSideEffect
{
get; set;
}
}
[TestClass]
public class ExampleFixture
{
[TestMethod]
public void DemonstrateThatTheClassRespondsToEvents()
{
var eventProvider = new Mock<IEventProvider>().Object;
var subject = new Example(eventProvider.Object);
Mock.Get(eventProvider)
.Raise( e => e.OnEvent += null, EventArgs.Empty);
Assert.IsTrue( subject.VisibleSideEffect,
"the visible side effect of the event was not raised.");
}
}
If you really need to test the implementation, there are other mechanisms available, such as a hand-rolled Test Spy / Test Double, or reflection-based strategy to get the delegate list. My hope is that you should be more concerned with class A's event handling logic than its event handler assignment. After all, if class A doesn't respond to the event and do something with it, the assignment shouldn't matter.
I don't know much about unit testing, but perhaps this link can give you some ideas. Note that the virtual keyword also works there.
I don't think moq has that capability - if you're prepared to purchase a tool I suggest you use Typemock Isolator that can verify that any method on an object was called - including event handler - have a look at link.
Take the following C# class:
c1 {
event EventHandler someEvent;
}
If there are a lot of subscriptions to c1's someEvent event and I want to clear them all, what is the best way to achieve this? Also consider that subscriptions to this event could be/are lambdas/anonymous delegates.
Currently my solution is to add a ResetSubscriptions() method to c1 that sets someEvent to null. I don't know if this has any unseen consequences.
From within the class, you can set the (hidden) variable to null. A null reference is the canonical way of representing an empty invocation list, effectively.
From outside the class, you can't do this - events basically expose "subscribe" and "unsubscribe" and that's it.
It's worth being aware of what field-like events are actually doing - they're creating a variable and an event at the same time. Within the class, you end up referencing the variable. From outside, you reference the event.
See my article on events and delegates for more information.
Add a method to c1 that will set 'someEvent' to null.
public class c1
{
event EventHandler someEvent;
public ResetSubscriptions() => someEvent = null;
}
class c1
{
event EventHandler someEvent;
ResetSubscriptions() => someEvent = delegate { };
}
It is better to use delegate { } than null to avoid the null ref exception.
The best practice to clear all subscribers is to set the someEvent to null by adding another public method if you want to expose this functionality to outside. This has no unseen consequences. The precondition is to remember to declare SomeEvent with the keyword 'event'.
Please see the book - C# 4.0 in the nutshell, page 125.
Some one here proposed to use Delegate.RemoveAll method. If you use it, the sample code could follow the below form. But it is really stupid. Why not just SomeEvent=null inside the ClearSubscribers() function?
public void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
// Then you will find SomeEvent is set to null.
}
Setting the event to null inside the class works. When you dispose a class you should always set the event to null, the GC has problems with events and may not clean up the disposed class if it has dangling events.
You can achieve this by using the Delegate.Remove or Delegate.RemoveAll methods.
Conceptual extended boring comment.
I rather use the word "event handler" instead of "event" or "delegate". And used the word "event" for other stuff. In some programming languages (VB.NET, Object Pascal, Objective-C), "event" is called a "message" or "signal", and even have a "message" keyword, and specific sugar syntax.
const
WM_Paint = 998; // <-- "question" can be done by several talkers
WM_Clear = 546;
type
MyWindowClass = class(Window)
procedure NotEventHandlerMethod_1;
procedure NotEventHandlerMethod_17;
procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
procedure DoClearEventHandler; message WM_Clear;
end;
And, in order to respond to that "message", a "event handler" respond, whether is a single delegate or multiple delegates.
Summary:
"Event" is the "question", "event handler (s)" are the answer (s).
Remove all events, assume the event is an "Action" type:
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}
This is my solution:
public class Foo : IDisposable
{
private event EventHandler _statusChanged;
public event EventHandler StatusChanged
{
add
{
_statusChanged += value;
}
remove
{
_statusChanged -= value;
}
}
public void Dispose()
{
_statusChanged = null;
}
}
You need to call Dispose() or use using(new Foo()){/*...*/} pattern to unsubscribe all members of invocation list.
Instead of adding and removing callbacks manually and having a bunch of delegate types declared everywhere:
// The hard way
public delegate void ObjectCallback(ObjectType broadcaster);
public class Object
{
public event ObjectCallback m_ObjectCallback;
void SetupListener()
{
ObjectCallback callback = null;
callback = (ObjectType broadcaster) =>
{
// one time logic here
broadcaster.m_ObjectCallback -= callback;
};
m_ObjectCallback += callback;
}
void BroadcastEvent()
{
m_ObjectCallback?.Invoke(this);
}
}
You could try this generic approach:
public class Object
{
public Broadcast<Object> m_EventToBroadcast = new Broadcast<Object>();
void SetupListener()
{
m_EventToBroadcast.SubscribeOnce((ObjectType broadcaster) => {
// one time logic here
});
}
~Object()
{
m_EventToBroadcast.Dispose();
m_EventToBroadcast = null;
}
void BroadcastEvent()
{
m_EventToBroadcast.Broadcast(this);
}
}
public delegate void ObjectDelegate<T>(T broadcaster);
public class Broadcast<T> : IDisposable
{
private event ObjectDelegate<T> m_Event;
private List<ObjectDelegate<T>> m_SingleSubscribers = new List<ObjectDelegate<T>>();
~Broadcast()
{
Dispose();
}
public void Dispose()
{
Clear();
System.GC.SuppressFinalize(this);
}
public void Clear()
{
m_SingleSubscribers.Clear();
m_Event = delegate { };
}
// add a one shot to this delegate that is removed after first broadcast
public void SubscribeOnce(ObjectDelegate<T> del)
{
m_Event += del;
m_SingleSubscribers.Add(del);
}
// add a recurring delegate that gets called each time
public void Subscribe(ObjectDelegate<T> del)
{
m_Event += del;
}
public void Unsubscribe(ObjectDelegate<T> del)
{
m_Event -= del;
}
public void Broadcast(T broadcaster)
{
m_Event?.Invoke(broadcaster);
for (int i = 0; i < m_SingleSubscribers.Count; ++i)
{
Unsubscribe(m_SingleSubscribers[i]);
}
m_SingleSubscribers.Clear();
}
}