Get a List of all Subscribers of an event [duplicate] - c#

I need to copy the subscribers of one event to another event. Can I get the subscribers of an event (like MyEvent[0] returning a delegate)?
If this is not possible I would use the add accessor to add the delegates to a list. Would that be the best solution?

C# events/delegates are multicast, so the delegate is itself a list. From within the class, to get individual callers, you can use:
if (field != null)
{
// or the event-name for field-like events
// or your own event-type in place of EventHandler
foreach(EventHandler subscriber in field.GetInvocationList())
{
// etc
}
}
However, to assign all at once, just use += or direct assignment:
SomeType other = ...
other.SomeEvent += localEvent;

If the event is one published by another class, you can't - at least, not reliably. While we often think of an event as being just a delegate variable, it's actually just a pair of methods: add and remove (or subscribe and unsubscribe).
If it's your own code that's publishing the event, it's easy - you can make the add/remove accessors do whatever you like.
Have a look at my article on events and see if that helps you. If not, please give more details about what you want to do, specifying which bits of code you're able to modify and which you aren't.

In case you need to examine subscribers of an external class' event:
EventHandler e = typeof(ExternalClass)
.GetField(nameof(ExternalClass.Event), BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(instanceOfExternalClass) as EventHandler;
if (e != null)
{
Delegate[] subscribers = e.GetInvocationList();
}

Update (thanks to commenters): delegate immutability means that cloning achieves nothing over an assignment.
When one writes:
myDelegate += AHandler
A completely new delegate instance is created and assigned to myDelegate.
Therefore, the code below would work exactly the same without the Clone call.
MulticastDelegate (the underlying type) has a Clone method.
To be able to get to the underlying delegate you might need to avoid the usual helper that the event keyword generates, and manage things directly (custom add and remove accessors).
To show this:
public class Program {
public delegate void MyDelegate(string name);
public event MyDelegate EventOne;
public void HandlerOne(string name) => Console.WriteLine($"This is handler one: {name}");
public void HandlerTwo(string name) => Console.WriteLine($"This is handler two: {name}");
public void HandlerThree(string name) => Console.WriteLine($"This is handler three: {name}");
public void Run() {
EventOne += HandlerOne;
EventOne += HandlerTwo;
Console.WriteLine("Before clone");
EventOne("EventOne");
MyDelegate eventTwo = (MyDelegate)EventOne.Clone();
MyDelegate eventTwo = EventOne;
Console.WriteLine("After clone copy");
EventOne("EventOne");
eventTwo("eventTwo");
Console.WriteLine("Change event one to show it is different");
EventOne += HandlerThree;
EventOne("EventOne");
eventTwo("eventTwo");
}
private static void Main(string[] args) => (new Program()).Run();
}

Related

How to check membership of an event handler from outside the owning class?

This question asks if there is a way to find if the code has already added its own event handler to an event. However, the answers given only work from inside the same class that own the event. (Delegate.GetInvocationList and others.)
I want to add a custom event handler to AppDomain.CurrentDomain.AssemblyResolve. Is there a way to find out if my custom handler is already added before adding it again? (For this and other standard library events.)
If the answer is indeed "That's impossible." then please give that as an answer.
That's impossible.
Basically, the only operations you have with an event from the outside are "subscribe" and "unsubscribe".
Now you could always unsubscribe before you subscribe. Unsubscribing is a no-op if the specified handler isn't already a handler for the event. If you make sure you always do that, then you'll definitely have exactly one handler subscribed. It does mean you need to be careful to do that everywhere you subscribe though - so ideally, put that code in one place.
(Alternatively, just change your event subscription so that you can easily tell that you'll only ever subscribe once...)
You can...
You can use reflection to get all delegates subscribed to the event and then check their names to see if yours is in there...
public class Foo
{
public event EventHandler MyEvent;
}
public class Bar
{
public static event EventHandler MyStaticEvent;
}
public class Test
{
public void MyDelegate(object sender, EventArgs e) { }
}
class Program
{
static void Main(string[] args)
{
Foo aFoo = new Foo();
Test aTest = new Test();
aFoo.MyEvent += aTest.MyDelegate;
FieldInfo subscribersReflect = typeof(Foo).GetField("MyEvent", BindingFlags.NonPublic | BindingFlags.Instance);
Delegate[] subscribers = (subscribersReflect.GetValue(aFoo) as MulticastDelegate).GetInvocationList();
foreach (var sub in subscribers)
Console.WriteLine(sub.Method.Name); // MyDelegate
Bar.MyStaticEvent += aTest.MyDelegate;
subscribersReflect = typeof(Bar).GetField("MyStaticEvent", BindingFlags.NonPublic | BindingFlags.Static);
subscribers = (subscribersReflect.GetValue(null) as MulticastDelegate).GetInvocationList();
foreach (var sub in subscribers)
Console.WriteLine(sub.Method.Name); // MyDelegate
Console.ReadLine();
}
}
...but you really shouldn't.
Any time you find yourself tempted to use reflection to go digging around in another class, especially a class you don't have the source for, and super-especially a framework class, that should be a warning signal that you're doing something wrong.
Jon Skeet's solution (unsubscribe then subscribe) is absolutely the correct solution to your problem, and is a good habit to be in any case. As he mentioned, unsubscribing a delegate that isn't subscribed has effectively no cost whatsoever, so any time you're unsure, go ahead and unsubscribe. It was designed that way specifically so that you could do that rather than using reflection.

How to remove yourself from an event handler?

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;
}
}

Checking existing 'wired up' methods

I may be misunderstanding something fundamental here as I'm new to these concepts so please bear with me.
I'm currently removing methods from an event like so:
scheduleView.TouchDown -= scheduleView_TouchDown;
And then on other occasions - adding the methods:
scheduleView.TouchDown += scheduleView_TouchDown;
It all works fine so far, and I can understand it's possible to add several methods, like so:
scheduleView.TouchDown += scheduleView_TouchDown;
scheduleView.TouchDown += scheduleView_AnotherTouchDownEventHandler;
But how would I then later check what methods were wired up to this event?
Interestingly, you can't (at least, from the outside). An event is only obliged to offer 2 accessors - add and remove. There are other accessor methods defined in the CLI spec, but they aren't used in C# or anywhere else AFAIK. The key point: we can't ask an event what is subscribed (and indeed, we shouldn't need to know). All you can do is: add or remove.
If you are worried about double-subscribing, then note that if you try to unsubscribe and you haven't actually subscribed, then under every sane implementation this is simply a no-op; which means you can do:
// make sure we are subscribed once but **only** once
scheduleView.TouchDown -= scheduleView_TouchDown;
scheduleView.TouchDown += scheduleView_TouchDown;
From the perspective of the code raising the event, you rarely need to know who - simply:
// note I'm assuming a "field-like event" implementation here; otherwise,
// change this to refer to the backing-field, or the delegate from the
// event-handler-list
var handler = TouchDown;
if(handler != null) handler(this, EventArgs.Empty); // or similar
There is also a way to break the delegate list into individual subscribers, but it is very rarely needed:
var handler = TouchDown;
if(handler != null) {
foreach(EventHandler subscriber in handler.GetInvocationList()) {
subscriber(this, EventArgs.Empty);
}
}
The main uses for this are:
when you want to perform exception-handling on a per-subscriber basis
when the delegate returns a value or changes state, and you need to handle that on a per-subscriber basis
Yes: If you are within the class that publishes the Event, you can just access the delegate, and you can call the GetInvocationList method to get a list of the subscribers.
No: If you are working outside the class, as the delegate is not exposed to you. You could use reflection to get at it, but that would be a hack, at best.
In the type that declares the event, you can use GetInvocationList() to find out which delegates are subscribed:
public class EventProvider
{
public event EventHandler SomeEvent;
protected virtual void OnSomeEvent(EventArgs args)
{
if (SomeEvent != null)
{
var delegates = SomeEvent.GetInvocationList();
foreach (var del in delegates)
{
Console.WriteLine("{0} has subscribed to SomeEvent", del.Method.Name);
}
SomeEvent(this, args);
}
}
public void RaiseSomeEvent()
{
OnSomeEvent(EventArgs.Empty);
}
}
public class Program
{
public static void Main()
{
EventProvider provider = new EventProvider();
provider.SomeEvent += Callback1;
provider.SomeEvent += Callback2;
provider.RaiseSomeEvent();
}
public static void Callback1(object sender, EventArgs e)
{
Console.WriteLine("Callback 1!");
}
public static void Callback2(object sender, EventArgs e)
{
Console.WriteLine("Callback 2!");
}
}
This produces the following output:
Callback1 has subscribed to SomeEvent
Callback2 has subscribed to SomeEvent
Callback 1!
Callback 2!

Event vs Delegates [duplicate]

What are the differences between delegates and an events? Don't both hold references to functions that can be executed?
An Event declaration adds a layer of abstraction and protection on the delegate instance. This protection prevents clients of the delegate from resetting the delegate and its invocation list and only allows adding or removing targets from the invocation list.
To understand the differences you can look at this 2 examples
Example with Delegates (in this case, an Action - that is a kind of delegate that doesn't return a value)
public class Animal
{
public Action Run {get; set;}
public void RaiseEvent()
{
if (Run != null)
{
Run();
}
}
}
To use the delegate, you should do something like this:
Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();
This code works well but you could have some weak spots.
For example, if I write this:
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;
with the last line of code, I have overridden the previous behaviors just with one missing + (I have used = instead of +=)
Another weak spot is that every class which uses your Animal class can invoke the delegate directly. For example, animal.Run() or animal.Run.Invoke() are valid outside the Animal class.
To avoid these weak spots you can use events in c#.
Your Animal class will change in this way:
public class ArgsSpecial : EventArgs
{
public ArgsSpecial (string val)
{
Operation=val;
}
public string Operation {get; set;}
}
public class Animal
{
// Empty delegate. In this way you are sure that value is always != null
// because no one outside of the class can change it.
public event EventHandler<ArgsSpecial> Run = delegate{}
public void RaiseEvent()
{
Run(this, new ArgsSpecial("Run faster"));
}
}
to call events
Animal animal= new Animal();
animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
Differences:
You aren't using a public property but a public field (using events, the compiler protects your fields from unwanted access)
Events can't be assigned directly. In this case, it won't give rise to the previous error that I have showed with overriding the behavior.
No one outside of your class can raise or invoke the event. For example, animal.Run() or animal.Run.Invoke() are invalid outside the Animal class and will produce compiler errors.
Events can be included in an interface declaration, whereas a field cannot
Notes:
EventHandler is declared as the following delegate:
public delegate void EventHandler (object sender, EventArgs e)
it takes a sender (of Object type) and event arguments. The sender is null if it comes from static methods.
This example, which uses EventHandler<ArgsSpecial>, can also be written using EventHandler instead.
Refer here for documentation about EventHandler
In addition to the syntactic and operational properties, there's also a semantical difference.
Delegates are, conceptually, function templates; that is, they express a contract a function must adhere to in order to be considered of the "type" of the delegate.
Events represent ... well, events. They are intended to alert someone when something happens and yes, they adhere to a delegate definition but they're not the same thing.
Even if they were exactly the same thing (syntactically and in the IL code) there will still remain the semantical difference. In general I prefer to have two different names for two different concepts, even if they are implemented in the same way (which doesn't mean I like to have the same code twice).
Here is another good link to refer to.
http://csharpindepth.com/Articles/Chapter2/Events.aspx
Briefly, the take away from the article - Events are encapsulation over delegates.
Quote from article:
Suppose events didn't exist as a concept in C#/.NET. How would another class subscribe to an event? Three options:
A public delegate variable
A delegate variable backed by a property
A delegate variable with AddXXXHandler and RemoveXXXHandler methods
Option 1 is clearly horrible, for all the normal reasons we abhor public variables.
Option 2 is slightly better, but allows subscribers to effectively override each other - it would be all too easy to write someInstance.MyEvent = eventHandler; which would replace any existing event handlers rather than adding a new one. In addition, you still need to write the properties.
Option 3 is basically what events give you, but with a guaranteed convention (generated by the compiler and backed by extra flags in the IL) and a "free" implementation if you're happy with the semantics that field-like events give you. Subscribing to and unsubscribing from events is encapsulated without allowing arbitrary access to the list of event handlers, and languages can make things simpler by providing syntax for both declaration and subscription.
What a great misunderstanding between events and delegates!!! A delegate specifies a TYPE (such as a class, or an interface does), whereas an event is just a kind of MEMBER (such as fields, properties, etc). And, just like any other kind of member an event also has a type. Yet, in the case of an event, the type of the event must be specified by a delegate. For instance, you CANNOT declare an event of a type defined by an interface.
Concluding, we can make the following Observation: the type of an event MUST be defined by a delegate. This is the main relation between an event and a delegate and is described in the section II.18 Defining events of ECMA-335 (CLI) Partitions I to VI:
In typical usage, the TypeSpec (if present) identifies a delegate whose signature matches the arguments passed to the event’s fire method.
However, this fact does NOT imply that an event uses a backing delegate field. In truth, an event may use a backing field of any different data structure type of your choice. If you implement an event explicitly in C#, you are free to choose the way you store the event handlers (note that event handlers are instances of the type of the event, which in turn is mandatorily a delegate type---from the previous Observation). But, you can store those event handlers (which are delegate instances) in a data structure such as a List or a Dictionary or any other else, or even in a backing delegate field. But don’t forget that it is NOT mandatory that you use a delegate field.
NOTE: If you have access to C# 5.0 Unleashed, read the "Limitations on Plain Use of Delegates" in Chapter 18 titled "Events" to understand better the differences between the two.
It always helps me to have a simple, concrete example. So here's one for the community. First I show how you can use delegates alone to do what Events do for us. Then I show how the same solution would work with an instance of EventHandler. And then I explain why we DON'T want to do what I explain in the first example. This post was inspired by an article by John Skeet.
Example 1: Using public delegate
Suppose I have a WinForms app with a single drop-down box. The drop-down is bound to an List<Person>. Where Person has properties of Id, Name, NickName, HairColor. On the main form is a custom user control that shows the properties of that person. When someone selects a person in the drop-down the labels in the user control update to show the properties of the person selected.
Here is how that works. We have three files that help us put this together:
Mediator.cs -- static class holds the delegates
Form1.cs -- main form
DetailView.cs -- user control shows all details
Here is the relevant code for each of the classes:
class Mediator
{
public delegate void PersonChangedDelegate(Person p); //delegate type definition
public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
{
if (PersonChangedDel != null)
{
PersonChangedDel(p);
}
}
}
Here is our user control:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.PersonChangedDel += DetailView_PersonChanged;
}
void DetailView_PersonChanged(Person p)
{
BindData(p);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Finally we have the following code in our Form1.cs. Here we are Calling OnPersonChanged, which calls any code subscribed to the delegate.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}
Ok. So that's how you would get this working without using events and just using delegates. We just put a public delegate into a class -- you can make it static or a singleton, or whatever. Great.
BUT, BUT, BUT, we do not want to do what I just described above. Because public fields are bad for many, many reason. So what are our options? As John Skeet describes, here are our options:
A public delegate variable (this is what we just did above. don't do this. i just told you above why it's bad)
Put the delegate into a property with a get/set (problem here is that subscribers could override each other -- so we could subscribe a bunch of methods to the delegate and then we could accidentally say PersonChangedDel = null, wiping out all of the other subscriptions. The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events.
A delegate variable with AddXXXHandler and RemoveXXXHandler methods
This third option is essentially what an event gives us. When we declare an EventHandler, it gives us access to a delegate -- not publicly, not as a property, but as this thing we call an event that has just add/remove accessors.
Let's see what the same program looks like, but now using an Event instead of the public delegate (I've also changed our Mediator to a singleton):
Example 2: With EventHandler instead of a public delegate
Mediator:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
public void OnPersonChanged(object sender, Person p)
{
var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
Notice that if you F12 on the EventHandler, it will show you the definition is just a generic-ified delegate with the extra "sender" object:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
The User Control:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
}
void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
{
BindData(e.Person);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Finally, here's the Form1.cs code:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}
Because the EventHandler wants and EventArgs as a parameter, I created this class with just a single property in it:
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
Hopefully that shows you a bit about why we have events and how they are different -- but functionally the same -- as delegates.
You can also use events in interface declarations, not so for delegates.
Delegate is a type-safe function pointer. Event is an implementation of publisher-subscriber design pattern using delegate.
An event in .net is a designated combination of an Add method and a Remove method, both of which expect some particular type of delegate. Both C# and vb.net can auto-generate code for the add and remove methods which will define a delegate to hold the event subscriptions, and add/remove the passed in delegagte to/from that subscription delegate. VB.net will also auto-generate code (with the RaiseEvent statement) to invoke the subscription list if and only if it is non-empty; for some reason, C# doesn't generate the latter.
Note that while it is common to manage event subscriptions using a multicast delegate, that is not the only means of doing so. From a public perspective, a would-be event subscriber needs to know how to let an object know it wants to receive events, but it does not need to know what mechanism the publisher will use to raise the events. Note also that while whoever defined the event data structure in .net apparently thought there should be a public means of raising them, neither C# nor vb.net makes use of that feature.
To define about event in simple way:
Event is a REFERENCE to a delegate with two restrictions
Cannot be invoked directly
Cannot be assigned values directly (e.g eventObj = delegateMethod)
Above two are the weak points for delegates and it is addressed in event. Complete code sample to show the difference in fiddler is here https://dotnetfiddle.net/5iR3fB .
Toggle the comment between Event and Delegate and client code that invokes/assign values to delegate to understand the difference
Here is the inline code.
/*
This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
Event is an delegate reference with two restrictions for increased protection
1. Cannot be invoked directly
2. Cannot assign value to delegate reference directly
Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/
public class RoomTemperatureController
{
private int _roomTemperature = 25;//Default/Starting room Temperature
private bool _isAirConditionTurnedOn = false;//Default AC is Off
private bool _isHeatTurnedOn = false;//Default Heat is Off
private bool _tempSimulator = false;
public delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
// public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public RoomTemperatureController()
{
WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
}
private void InternalRoomTemperatuerHandler(int roomTemp)
{
System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
}
//User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
public bool TurnRoomTeperatureSimulator
{
set
{
_tempSimulator = value;
if (value)
{
SimulateRoomTemperature(); //Turn on Simulator
}
}
get { return _tempSimulator; }
}
public void TurnAirCondition(bool val)
{
_isAirConditionTurnedOn = val;
_isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
}
public void TurnHeat(bool val)
{
_isHeatTurnedOn = val;
_isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
}
public async void SimulateRoomTemperature()
{
while (_tempSimulator)
{
if (_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned On
if (_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned On
System.Console.WriteLine("Temperature :" + _roomTemperature);
if (WhenRoomTemperatureChange != null)
WhenRoomTemperatureChange(_roomTemperature);
System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
}
}
}
public class MySweetHome
{
RoomTemperatureController roomController = null;
public MySweetHome()
{
roomController = new RoomTemperatureController();
roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
//roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
//roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
roomController.SimulateRoomTemperature();
System.Threading.Thread.Sleep(5000);
roomController.TurnAirCondition (true);
roomController.TurnRoomTeperatureSimulator = true;
}
public void TurnHeatOrACBasedOnTemp(int temp)
{
if (temp >= 30)
roomController.TurnAirCondition(true);
if (temp <= 15)
roomController.TurnHeat(true);
}
public static void Main(string []args)
{
MySweetHome home = new MySweetHome();
}
}
For people live in 2020, and want a clean answer...
Definitions:
delegate: defines a function pointer.
event: defines
(1) protected interfaces, and
(2) operations(+=, -=), and
(3) advantage: you don't need to use new keyword anymore.
Regarding the adjective protected:
// eventTest.SomeoneSay = null; // Compile Error.
// eventTest.SomeoneSay = new Say(SayHello); // Compile Error.
Also notice this section from Microsoft: https://learn.microsoft.com/en-us/dotnet/standard/events/#raising-multiple-events
Code Example:
with delegate:
public class DelegateTest
{
public delegate void Say(); // Define a pointer type "void <- ()" named "Say".
private Say say;
public DelegateTest() {
say = new Say(SayHello); // Setup the field, Say say, first.
say += new Say(SayGoodBye);
say.Invoke();
}
public void SayHello() { /* display "Hello World!" to your GUI. */ }
public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
}
with event:
public class EventTest
{
public delegate void Say();
public event Say SomeoneSay; // Use the type "Say" to define event, an
// auto-setup-everything-good field for you.
public EventTest() {
SomeoneSay += SayHello;
SomeoneSay += SayGoodBye;
SomeoneSay();
}
public void SayHello() { /* display "Hello World!" to your GUI. */ }
public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
}
Reference:
Event vs. Delegate - Explaining the important differences between the Event and Delegate patterns in C# and why they're useful.: https://dzone.com/articles/event-vs-delegate

C# How to find if an event is hooked up

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.

Categories