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");
}
Related
I have an event broker that exposes an EventHandler<T> that allows observers to inspect the event argument and, if needed, modify it. While this works okay, I would ideally like to ensure that T only lives on the stack and, furthermore, that no component is able to take a reference to T, thereby extending its lifetime.
public class Game // mediator pattern
{
public event EventHandler<Query> Queries; // effectively a chain
public void PerformQuery(object sender, Query q)
{
Queries?.Invoke(sender, q);
}
}
Sadly, a ref struct cannot be used as a generic argument:
ref struct Query {} // EventHandler<Query> not allowed
And similarly I cannot imbue EventHandler's TEventArgs with any sort of 'use structs, pass by reference' mechanics.
Now, in C#, we can decide whether variables live on the stack on the heap, e.g. with stackalloc and such, so what I'm after, I guess, is just a way of getting something equivalent to a ref struct inside an event.
While stackalloc applied/wrapped in a very convoluted way may (probably) give you some semblance of variables live on the stack on the heap, it will not be what stackalloc is intended for.
So I'd rather propose to concentrate on the no component is able to take a reference to T, thereby extending its lifetime part.
To get it we need
Wrapper class (probably, but not necessary with corresponding interface)
Implementing IDisposable
And storing the actual T as WeakReference
It will be something like
public interface ITakeNoRefClass
{
void Change(string value);
}
public class TakeNoRefClass : ITakeNoRefClass
{
...
}
public class TakeNoRefClassWrapper : ITakeNoRefClass, IDisposable
{
private bool _isDisposed;
private readonly WeakReference<TakeNoRefClass> _takeNoRefWeakRef;
public TakeNoRefClassWrapper(WeakReference<TakeNoRefClass> takeNoRefWeakRef)
{
_takeNoRefWeakRef = takeNoRefWeakRef;
}
public void Change(string value)
{
Execute(o => o.Change(value));
}
private void Execute(Action<ITakeNoRefClass> action)
{
if (_disposed)
{
throw new ObjectDisposedException("You should not have taken this ref");
}
var target = _takeNoRefWeakRef.Target;
if (target == null)
{
throw new ObjectDisposedException("You should not have taken this ref");
}
action(target);
}
public void Dispose()
{
_isDisposed = true;
}
}
And it should be used like
public void CreateObjectAndRaiseEvents()
{
var target = new TakeNoRefClass();
// Passing it into a separate method to ensure that it won't be GC'ed before executing all event handlers.
RaiseEvents(target);
}
private void RaiseEvent(TakeNoRefClass target)
{
using (var wrapper = new TakeNoRefClassWrapper(new WeakReference<TakeNoRefClass>(target))
{
_event?.Invoke(wrapper);
}
}
I have a class, EventContainer.cs, which contains an event, say:
public event EventHandler AfterSearch;
I have another class, EventRaiser.cs. How do I raise (and not handle) the above said event from this class?
The raised event will in turn call the handler of the event in the EventContainer class. Something like this (this is obviously not correct):
EventContainer obj = new EventContainer();
RaiseEvent(obj.AfterSearch);
This is not possible, Events can only be risen from inside the class. If you could do that, it would defeat the purpose of events (being able to rise status changes from inside the class). I think you are misunderstanding the function of events - an event is defined inside a class and others can subscribe to it by doing
obj.AfterSearch += handler; (where handler is a method according to the signature of AfterSearch). One is able to subscribe to the event from the outside just fine, but it can only be risen from inside the class defining it.
It is POSSIBLE, but using clever hack.
Inspired by http://netpl.blogspot.com/2010/10/is-net-type-safe.html
If you don't believe, try this code.
using System;
using System.Runtime.InteropServices;
namespace Overlapping
{
[StructLayout(LayoutKind.Explicit)]
public class OverlapEvents
{
[FieldOffset(0)]
public Foo Source;
[FieldOffset(0)]
public OtherFoo Target;
}
public class Foo
{
public event EventHandler Clicked;
public override string ToString()
{
return "Hello Foo";
}
public void Click()
{
InvokeClicked(EventArgs.Empty);
}
private void InvokeClicked(EventArgs e)
{
var handler = Clicked;
if (handler != null)
handler(this, e);
}
}
public class OtherFoo
{
public event EventHandler Clicked;
public override string ToString()
{
return "Hello OtherFoo";
}
public void Click2()
{
InvokeClicked(EventArgs.Empty);
}
private void InvokeClicked(EventArgs e)
{
var handler = Clicked;
if (handler != null)
handler(this, e);
}
public void Clean()
{
Clicked = null;
}
}
class Test
{
public static void Test3()
{
var a = new Foo();
a.Clicked += AClicked;
a.Click();
var o = new OverlapEvents { Source = a };
o.Target.Click2();
o.Target.Clean();
o.Target.Click2();
a.Click();
}
static void AClicked(object sender, EventArgs e)
{
Console.WriteLine(sender.ToString());
}
}
}
You can write a public method on the class you want the event to fire from and fire the event when it is called. You can then call this method from whatever user of your class.
Of course, this ruins encapsulation and is bad design.
It looks like you're using the Delegate pattern. In this case, the AfterSearch event should be defined on the EventRaiser class, and the EventContainer class should consume the event:
In EventRaiser.cs
public event EventHandler BeforeSearch;
public event EventHandler AfterSearch;
public void ExecuteSearch(...)
{
if (this.BeforeSearch != null)
this.BeforeSearch();
// Do search
if (this.AfterSearch != null)
this.AfterSearch();
}
In EventContainer.cs
public EventContainer(...)
{
EventRaiser er = new EventRaiser();
er.AfterSearch += this.OnAfterSearch;
}
public void OnAfterSearch()
{
// Handle AfterSearch event
}
I stumbled across this problem as well, because i was experimenting with calling PropertyChanged events from outside. So you dont have to implement everything in every class. The solution from halorty wouldn't work using interfaces.
I found a solution working using heavy reflection. It is surely slow and is breaking the principle that events should only be called from inside a class. But it is interesting to find a generic solution to this problem....
It works because every event is a list of invocation methods being called.
So we can get the invocation list and call every listener attached to that event by our own.
Here you go....
class Program
{
static void Main(string[] args)
{
var instance = new TestPropertyChanged();
instance.PropertyChanged += PropertyChanged;
instance.RaiseEvent(nameof(INotifyPropertyChanged.PropertyChanged), new PropertyChangedEventArgs("Hi There from anywhere"));
Console.ReadLine();
}
private static void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
}
public static class PropertyRaiser
{
private static readonly BindingFlags staticFlags = BindingFlags.Instance | BindingFlags.NonPublic;
public static void RaiseEvent(this object instance, string eventName, EventArgs e)
{
var type = instance.GetType();
var eventField = type.GetField(eventName, staticFlags);
if (eventField == null)
throw new Exception($"Event with name {eventName} could not be found.");
var multicastDelegate = eventField.GetValue(instance) as MulticastDelegate;
if (multicastDelegate == null)
return;
var invocationList = multicastDelegate.GetInvocationList();
foreach (var invocationMethod in invocationList)
invocationMethod.DynamicInvoke(new[] {instance, e});
}
}
public class TestPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
There is good way to do this. Every event in C# has a delegate that specifies the sign of methods for that event. Define a field in your external class with type of your event delegate. get the the reference of that field in the constructor of external class and save it. In main class of your event, send the reference of event for delegate of external class. Now you can easily call the delegate in your external class.
public delegate void MyEventHandler(object Sender, EventArgs Args);
public class MyMain
{
public event MyEventHandler MyEvent;
...
new MyExternal(this.MyEvent);
...
}
public MyExternal
{
private MyEventHandler MyEvent;
public MyExternal(MyEventHandler MyEvent)
{
this.MyEvent = MyEvent;
}
...
this.MyEvent(..., ...);
...
}
Agree with Femaref -- and note this is an important difference between delegates and events (see for example this blog entry for an good discussion of this and other differences).
Depending on what you want to achieve, you might be better off with a delegate.
Not a good programming but if you want to do that any way you can do something like this
class Program
{
static void Main(string[] args)
{
Extension ext = new Extension();
ext.MyEvent += ext_MyEvent;
ext.Dosomething();
}
static void ext_MyEvent(int num)
{
Console.WriteLine(num);
}
}
public class Extension
{
public delegate void MyEventHandler(int num);
public event MyEventHandler MyEvent;
public void Dosomething()
{
int no = 0;
while(true){
if(MyEvent!=null){
MyEvent(++no);
}
}
}
}
I had a similar confusion and honestly find the answers here to be confusing. Although a couple hinted at solutions that I would later find would work.
My solution was to hit the books and become more familiar with delegates and event handlers.
Although I've used both for many years, I was never intimately familiar with them.
http://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices
gives the best explanation of both delegates and event handlers that I've ever read and clearly explains that a class can be a publisher of events and have other classes consume them.
This article: http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single discusses how to single-cast events to only one handler since delegates are multicast by definition . A delegate inherits system.MulticastDelegate most including the system delegates are Multicast.
I found that multicast meant that any event handler with the same signature would receive the raised event. Multicast behavior has caused me some sleepless nights as I stepped through code and saw my event seemingly erroneously being sent to handlers that I had no intention of getting this event. Both articles explains this behavior.
The second article shows you one way, and the first article shows you another, by making the delegate and the signature tightly typed.
I personally believe strong typing prevents stupid bugs that can be a pain to find. So I'd vote for the first article, even though I got the second article code working. I was just curious. :-)
I also got curious if I could get #2 articles code to behave like how I interpreted the original question above. Regardless of your chosen approach or if I'm also misinterpreting the original question, my real message is that I still think you would benefit from reading the first article as I did, especially if the questions or answers on this page leave you confused. If you are having multicast nightmares and need a quick solution then article 2 may help you.
I started playing with the second article's eventRaiser class. I made a simple windows form project.
I added the second articles class EventRaiser.cs to my project.
In the Main form's code, I defined a reference to that EventRaiser class at the top as
private EventRaiser eventRaiser = new EventRaiser();
I added a method in the main form code that I wanted to be called when the event was fired
protected void MainResponse( object sender, EventArgs eArgs )
{
MessageBox.Show("got to MainResponse");
}
then in the main form's constructor I added the event assignment:
eventRaiser.OnRaiseEvent += new EventHandler(MainResponse);`
I then created a class that would be instantiated by my main form called "SimpleClass" for lack of creative ingenuity at the moment.
Then I added a button and in the button's click event
I instantiated the SimpleClass code I wanted to raise an event from:
private void button1_Click( object sender, EventArgs e )
{
SimpleClass sc = new SimpleClass(eventRaiser);
}
Note the instance of "eventRaiser" that I passed to SimpleClass.cs. That was defined and instantiated earlier in the Main form code.
In the SimpleClass:
using System.Windows.Forms;
using SinglecastEvent; // see SingleCastEvent Project for info or http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single
namespace GenericTest
{
public class SimpleClass
{
private EventRaiser eventRaiser = new EventRaiser();
public SimpleClass( EventRaiser ev )
{
eventRaiser = ev;
simpleMethod();
}
private void simpleMethod()
{
MessageBox.Show("in FileWatcher.simple() about to raise the event");
eventRaiser.RaiseEvent();
}
}
}
The only point to the private method I called SimpleMethod was to verify that a privately scoped method could still raise the event, not that I doubted it, but I like to be positive.
I ran the project and this resulted in raising the event from the "simpleMethod" of the "SimpleClass" up to the main form and going to the expected correct method called MainResponse proving that one class can indeed raise an event that is consumed by a different class.
Yes the event has to be raised from within the class that needs it's change broadcast to other classes that care. Receiving classes can be one class or many many classes depending on how strongly typed you defined them or by making them single cast as in 2nd article.
Hope this helps and not muddy the water. Personally I've got a lot of delegates and events to clean up! Multicast demons begone!
The raising class has to get a fresh copy of the EventHandler.
One possible solution below.
using System;
namespace ConsoleApplication1
{
class Program
{
class HasEvent
{
public event EventHandler OnEnvent;
EventInvoker myInvoker;
public HasEvent()
{
myInvoker = new EventInvoker(this, () => OnEnvent);
}
public void MyInvokerRaising() {
myInvoker.Raise();
}
}
class EventInvoker
{
private Func<EventHandler> GetEventHandler;
private object sender;
public EventInvoker(object sender, Func<EventHandler> GetEventHandler)
{
this.sender = sender;
this.GetEventHandler = GetEventHandler;
}
public void Raise()
{
if(null != GetEventHandler())
{
GetEventHandler()(sender, new EventArgs());
}
}
}
static void Main(string[] args)
{
HasEvent h = new HasEvent();
h.OnEnvent += H_OnEnvent;
h.MyInvokerRaising();
}
private static void H_OnEnvent(object sender, EventArgs e)
{
Console.WriteLine("FIRED");
}
}
}
Use public EventHandler AfterSearch;
not
public event EventHandler AfterSearch;
Use a Delegate (an Action or Func) instead of an event. An event is essentially a delegate that can only be triggered from within the class.
I took a slightly different approach in solving this problem. My solution consisted of a winform front end, a main Class Library (DLL) and within that dll, a secondary working class:
WinForm
|------> PickGen Library
|---------> Allocations class
What I decided to do is to create events in the main dll (PickGen) that the Allocations class could call, then those event methods would called the events within the UI.
So, allocations raises an event in PickGen which takes the parameter values and raises the event in the form. From a code standpoint, this is in the lowest class:
public delegate void AllocationService_RaiseAllocLog(string orderNumber, string message, bool logToDatabase);
public delegate void AllocationService_RaiseAllocErrorLog(string orderNumber, string message, bool logToDatabase);
public class AllocationService { ...
public event AllocationService_RaiseAllocLog RaiseAllocLog;
public event AllocationService_RaiseAllocErrorLog RaiseAllocErrorLog;
then later in the subclass code:
RaiseAllocErrorLog(SOHNUM_0, ShipmentGenerated + ": Allocated line QTY was: " + allocatedline.QTY_0 + ", Delivered was: " + QTY_0 + ". Problem batch.", false);
In the main DLL Class library I have these two event methods:
private void PickGenLibrary_RaiseAllocLog(string orderNumber, string message, bool updateDB)
{
RaiseLog(orderNumber, message, false);
}
private void PickGenLibrary_RaiseAllocErrorLog(string orderNumber, string message, bool updateDB)
{
RaiseErrorLog(orderNumber, message, false);
}
and I make the connection here when I create the allocation object:
AllocationService allsvc = new AllocationService(PickResult);
allsvc.RaiseAllocLog += new AllocationService_RaiseAllocLog(PickGenLibrary_RaiseAllocLog);
allsvc.RaiseAllocErrorLog += new AllocationService_RaiseAllocErrorLog(PickGenLibrary_RaiseAllocErrorLog);
and I also then have delegates that are set up to tie the main class with the winform code:
public delegate void JPPAPickGenLibrary_RaiseLog(string orderNumber, string message, bool logToDatabase);
public delegate void JPPAPickGenLibrary_RaiseErrorLog(string orderNumber, string message, bool logToDatabase);
It may not be the most elegant way to do it, but in the end, it does work and without being too obscure.
A nested class with an instance of the outer class provided in the constructor can access even private members of the outer class. As explained more here: stackoverflow question on inner classes.
This includes the ability to raise events in the outer class. This EventRaisers class could be internal, or otherwise controlled somehow, because it could technically otherwise be created by any script with a reference to the outer class instance.
Very simple example. i like to do it this way using EventHandler.
class Program
{
static void Main(string[] args)
{
MyExtension ext = new MyExtension();
ext.MyEvent += ext_MyEvent;
ext.Dosomething();
Console.ReadLine();
}
static void ext_MyEvent(object sender, int num)
{
Console.WriteLine("Event fired.... "+num);
}
}
public class MyExtension
{
public event EventHandler<int> MyEvent;
public void Dosomething()
{
int no = 1;
if (MyEvent != null)
MyEvent(this, ++no);
}
}
}
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;
}
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.
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();
}
}