C# is still not OO enough? Here I'm giving a (maybe bad) example.
public class Program
{
public event EventHandler OnStart;
public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts");
public class MyCSharpProgram
{
public string Name { get; set; }
public event EventHandler OnStart;
public void Start()
{
OnStart(this, EventArgs.Empty);
}
}
static void Main(string[] args)
{
MyCSharpProgram cs = new MyCSharpProgram { Name = "C# test" };
cs.OnStart += LogOnStart; //can compile
//RegisterLogger(cs.OnStart); // Line of trouble
cs.Start(); // it prints "start" (of course it will :D)
Program p = new Program();
RegisterLogger(p.OnStart); //can compile
p.OnStart(p, EventArgs.Empty); //can compile, but NullReference at runtime
Console.Read();
}
static void RegisterLogger(EventHandler ev)
{
ev += LogOnStart;
}
}
RegisterLogger(cs.OnStart) leads to compile error, because "The event XXX can only appear on the left hand side of += or -= blabla". But why RegisterLogger(p.OnStart) can? Meanwhile, although I registered p.OnStart, it will also throw an NullReferenceException, seems that p.OnStart is not "really" passed to a method.
"The event XXX can only appear on the left hand side of += or -= blabla"
This is actually because C# is "OO enough." One of the core principles of OOP is encapsulation; events provide a form of this, just like properties: inside the declaring class they may be accessed directly, but outside they are only exposed to the += and -= operators. This is so that the declaring class is in complete control of when the events are called. Client code can only have a say in what happens when they are called.
The reason your code RegisterLogger(p.OnStart) compiles is that it is declared from within the scope of the Program class, where the Program.OnStart event is declared.
The reason your code RegisterLogger(cs.OnStart) does not compile is that it is declared from within the scope of the Program class, but the MyCSharpProgram.OnStart event is declared (obviously) within the MyCSharpProgram class.
As Chris Taylor points out, the reason you get a NullReferenceException on the line p.OnStart(p, EventArgs.Empty); is that calling RegisterLogger as you have it assigns a new value to a local variable, having no affect on the object to which that local variable was assigned when it was passed in as a parameter. To understand this better, consider the following code:
static void IncrementValue(int value)
{
value += 1;
}
int i = 0;
IncrementValue(i);
// Prints '0', because IncrementValue had no effect on i --
// a new value was assigned to the COPY of i that was passed in
Console.WriteLine(i);
Just as a method that takes an int as a parameter and assigns a new value to it only affects the local variable copied to its stack, a method that takes an EventHandler as a parameter and assigns a new value to it only affects its local variable as well (in an assignment).
Make the following change to RegisterLogger, declare ev as a reference argument to the event handler.
static void RegisterLogger(ref EventHandler ev)
{
ev += LogOnStart;
}
Then your call point will also need to use the 'ref' keyword when invoking the method as follows
RegisterLogger(ref p.OnStart);
The reason this fails to compile:
RegisterLogger(cs.OnStart);
... is that the event handler and the method you are passing it to are in different classes. C# treats events very strictly, and only allows the class that the event appears in to do anything other than add a handler (including pass it to functions, or invoke it).
For example, this won't compile either (because it is in a different class):
cs.OnStart(cs, EventArgs.Empty);
As for not being able to pass an event handler to a function this way, I'm not sure. I am guessing events operate like value types. Passing it by ref will fix your problem, though:
static void RegisterLogger(ref EventHandler ev)
{
ev += LogOnStart;
}
When an object declares an event, it only exposes methods to add and/or remove handlers to the event outside of the class (provided it doesn't redefine the add/remove operations). Within it, it is treated much like an "object" and works more or less like a declared delegate variable. If no handlers are added to the event, it's as if it were never initialized and is null. It is this way by design. Here is the typical pattern used in the framework:
public class MyCSharpProgram
{
// ...
// define the event
public event EventHandler SomeEvent;
// add a mechanism to "raise" the event
protected virtual void OnSomeEvent()
{
// SomeEvent is a "variable" to a EventHandler
if (SomeEvent != null)
SomeEvent(this, EventArgs.Empty);
}
}
// etc...
Now if you must insist on exposing the delegate outside of your class, just don't define it as an event. You could then treat it as any other field or property.
I've modified your sample code to illustrate:
public class Program
{
public EventHandler OnStart;
public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts");
public class MyCSharpProgram
{
public string Name { get; set; }
// just a field to an EventHandler
public EventHandler OnStart = (s, e) => { /* do nothing */ }; // needs to be initialized to use "+=", "-=" or suppress null-checks
public void Start()
{
// always check if non-null
if (OnStart != null)
OnStart(this, EventArgs.Empty);
}
}
static void Main(string[] args)
{
MyCSharpProgram cs = new MyCSharpProgram { Name = "C# test" };
cs.OnStart += LogOnStart; //can compile
RegisterLogger(cs.OnStart); // should work now
cs.Start(); // it prints "start" (of course it will :D)
Program p = new Program();
RegisterLogger(p.OnStart); //can compile
p.OnStart(p, EventArgs.Empty); //can compile, but NullReference at runtime
Console.Read();
}
static void RegisterLogger(EventHandler ev)
{
// Program.OnStart not initialized so ev is null
if (ev != null) //null-check just in case
ev += LogOnStart;
}
}
Related
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);
}
}
}
What I want to do is basically remove a function from an event, without knowing the function's name.
I have a FileSystemWatcher. If a file is created/renamed it checks its name. If it matches, it then moves it to a specific location. However, if the file is locked, it makes a lambda that attaches to a timer's tick event, waiting until the file is not locked. When it isn't, it moves the file and then removes itself from the event handler. I've seen lots of ways to do this, like keeping the instance, or making a named method. I can't do either of those here. What are my options?
There is no simple method to achieve this.
Preferred approach:
I don't see why you can't save the delegate. You don't have to save the instance as some field. It can be a local variable that is captured by your anonymous event handler:
EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
// Do whatever you need to do here
// Remove event:
foo.Event -= handler;
}
foo.Event += handler;
I can't think of a single scenario where you can't use this.
Alternative approach without saving the delegate:
However, if you have such a scenario, it get's quite tricky.
You need to find the delegate that has been added as a handler to the event. Because you didn't save it, it is pretty hard to obtain it. There is no this to get a delegate of the currently executing method.
You can't use GetInvocationList() on the event either, because accessing an event outside the class it is defined in is restricted to adding and removing handlers, i.e. += and -=.
Creating a new delegate isn't possible either. While you can get access to the MethodInfo object defining your anonymous method, you can't get access to the instance of the class that method is declared in. This class is generated automatically by the compiler and calling this inside the anonymous method will return the instance of the class your normal method is defined in.
The only way I found that works is to find the field - if any - that the event uses and call GetInvocationList() on it. The following code demonstrates this with a dummy class:
void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => {
Console.WriteLine("Executed");
var self = new StackFrame().GetMethod();
var eventField = foo.GetType()
.GetField("Bar", BindingFlags.NonPublic |
BindingFlags.Instance);
if(eventField == null)
return;
var eventValue = eventField.GetValue(foo) as EventHandler;
if(eventValue == null)
return;
var eventHandler = eventValue.GetInvocationList()
.OfType<EventHandler>()
.FirstOrDefault(x => x.Method == self)
as EventHandler;
if(eventHandler != null)
foo.Bar -= eventHandler;
};
foo.RaiseBar();
foo.RaiseBar();
}
public class Foo
{
public event EventHandler Bar;
public void RaiseBar()
{
var handler = Bar;
if(handler != null)
handler(this, EventArgs.Empty);
}
}
Please note that the string "Bar" that is passed to GetField needs to be the exact name of the field that is used by the event. This results in two problems:
The field can be named differently, e.g. when using an explicit event implementation. You need to manually find out the field name.
There might be no field at all. This happens if the event uses an explicit event implementation and just delegates to another event or stores the delegates in some other way.
Conclusion:
The alternative approach relies on implementation details, so don't use it if you can avoid it.
Steps to remove event handler with lambda expression:
public partial class Form1 : Form
{
private dynamic myEventHandler;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there"));
this.button1.Click += myEventHandler;
}
private void button1_Click(object sender, EventArgs e, string additionalInfo)
{
MessageBox.Show(additionalInfo);
button1.Click -= myEventHandler;
}
}
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.
I want to be able to find out if an event is hooked up or not. I've looked around, but I've only found solutions that involved modifying the internals of the object that contains the event. I don't want to do this.
Here is some test code that I thought would work:
// Create a new event handler that takes in the function I want to execute when the event fires
EventHandler myEventHandler = new EventHandler(myObject_SomeEvent);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length;
// Now actually hook an event up
myObject.SomeEvent += m_myEventHandler;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length;
Unfort the above is dead wrong. I thought that somehow the "invocationList" in myEventHandler would automatically get updated when I hooked an event to it. But no, this is not the case. The length of this always comes back as one.
Is there anyway to determine this from outside the object that contains the event?
If the object concerned has specified the event keyword, then the only things you can do are add (+=) and remove (-=) handlers, nothing more.
I believe that comparing the invocation list length would work, but you need to be operating inside the object to get at it.
Also, keep in mind that the += and -= operators return a new event object; they don't modify an existing one.
Why do you want to know if a particular event is hooked up? Is it to avoid registering multiple times?
If so, the trick is to remove the handler first (-=) as removing a handler that's not there is legal, and does nothing. Eg:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
There is a subtle illusion presented by the C# event keyword and that is that an event has an invocation list.
If you declare the event using the C# event keyword, the compiler will generate a private delegate in your class, and manage it for you. Whenever you subscribe to the event, the compiler-generated add method is invoked, which appends the event handler to the delegate's invocation list. There is no explicit invocation list for the event.
Thus, the only way to get at the delegate's invocation list is to preferably:
Use reflection to access the compiler-generated delegate OR
Create a non-private delegate (perhaps internal) and implement the event's add/remove methods manually (this prevents the compiler from generating the event's default implementation)
Here is an example demonstrating the latter technique.
class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}
It can be done, but it takes some hackery... as mentioned above the compiler generates the implementation of the event, including its backing field. Reflection lets you retrieve the backing field by name, and once you have access to it you can call GetInvocationList() even though you're outside the class itself.
Since you're asking to use reflection to get the event by name I assume you're also using reflection to get the Type by name--I'm whipping up an example that will show how to do it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string typeName = "ConsoleApplication1.SomeClass, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
string eventName = "SomeEvent";
Type declaringType = Type.GetType(typeName);
object target = Activator.CreateInstance(declaringType);
EventHandler eventDelegate;
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null) { Console.WriteLine("No listeners"); }
// attach a listener
SomeClass bleh = (SomeClass)target;
bleh.SomeEvent += delegate { };
//
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null)
{
Console.WriteLine("No listeners");
}
else
{
Console.WriteLine("Listeners: " + eventDelegate.GetInvocationList().Length);
}
Console.ReadKey();
}
static EventHandler GetEventHandler(object classInstance, string eventName)
{
Type classType = classInstance.GetType();
FieldInfo eventField = classType.GetField(eventName, BindingFlags.GetField
| BindingFlags.NonPublic
| BindingFlags.Instance);
EventHandler eventDelegate = (EventHandler)eventField.GetValue(classInstance);
// eventDelegate will be null if no listeners are attached to the event
if (eventDelegate == null)
{
return null;
}
return eventDelegate;
}
}
class SomeClass
{
public event EventHandler SomeEvent;
}
}
You should be able to get the invocation list via the "event". Roughly, it will be something like..
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
}
I used your example and modified it a little bit. registering an event handler increases the number of invocations. even when using two different callback methods (as shown here) or using the same callback method.
private void SomeMethod()
{
// Create a new event handler that takes in the function I want to execute when the event fires
var myEventHandler = new EventHandler(OnPropertyChanged);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length; // 1
// Now actually hook an event up
myEventHandler += OnPropertyChanged2;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length; // 2
myEventHandler.Invoke(null, null);
// each of the registered callback methods are executed once.
// or if the same callback is used, then twice.
}
private void OnPropertyChanged2(object? sender, EventArgs e)
{}
private void OnPropertyChanged(object? sender, EventArgs e)
{}
As others already mentioned, the access to eventhandler.GetInvocationList is limited to the class itself, you need to expose a property or method to retrieve the delegate list.
Like this:
protected Delegate[]? GetInvocations() => PropertyChanged?.GetInvocationList();
depending on your usage make it protected, internal or both.
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();
}
}