The following methods are part of a base class which enables derived classes to specify who should be notified by an event.
protected void RaiseEvent<TEventArgs>(EventHandler<TEventArgs> updateEvent, TEventArgs eventArgs, UpdateReceivers updateReceivers)
where TEventArgs : EventArgs
{
EventHandler<TEventArgs> handler = updateEvent;
if (handler != null)
{
if (updateReceivers.ToAllSubscribers)
{
handler(this, eventArgs);
}
else
{
NotifySpecifiedReceiver(handler, eventArgs, updateReceivers.Receiver);
}
}
}
private void NotifySpecifiedReceiver<TEventArgs>(EventHandler<TEventArgs> handler, TEventArgs eventArgs, object updateReceiver)
where TEventArgs : EventArgs
{
foreach (Delegate #delegate in handler.GetInvocationList())
{
// is the delegates target our receiver?
// but this doesnt work for anonymous methods :(
if (#delegate.Target == updateReceiver)
{
try
{
#delegate.DynamicInvoke(this, eventArgs);
}
catch (Exception ex)
{
}
}
}
}
To notify only a specific receiver, the method is used like this: (the receiver has to be subscribed of course)
event EventHandler<SomethingChangedEventArgs> SomethingChanged;
RaiseEvent(SomethingChanged, args, UpdateReceivers.Single(receiver));
This will only raise the delegates "pointing" to the receiver.
My problem here is when i use a anonymous method to subscribe to the SomethingChanged event its doesnt work when i use this event to notify the object which subscribed to it.
class EventConsumer
{
private EventSource _eventSource;
private void SubscribeEvents()
{
// ReactOnEvent() will not be called because the check [#delegate.Target == updateReceiver] doesnt work for anonymous methods
_eventSource.MyStateChanged += (sender, e) => ReactOnEvent();
_eventSource.PublishCurrentState(this);
}
}
class EventSource
{
// multiple objects are subscribed to this event
event EventHandler<MyStateChangedEventArgs> MyStateChanged;
public void GetCurrentState(object receiver)
{
// receiver ask for the current state, only raise event for him
RaiseEvent(MyStateChanged, args, UpdateReceivers.Single(receiver));
}
}
Is it possible the get the instance containing a anonymous method? or should i use a complete different approach to solve my problem?
You're (presumably) seeing the compiler-generated closure class that contains variables used by the anonymous method.
This class has specifically-named fields that reference information from the parent class.
You can use reflection to find a field in the compiler-generated DisplayClass (the Target value) named <>4__this, and get its value to find the instance of the class that created the delegate.
However, don't do this.
This relies on an internal behavior of the C# compiler that may change at any time.
Also, the fields contained by the closure class depend on where the anonymous method is and what members it references. If the anonymous method doesn't use the class instance, it may not have a this field at all.
Related
I have some code where I need to dynamically bind events to an event handler:
foreach (string evnt in attribute.Events)
{
EventInfo ei = control.GetType().GetEvent(evnt);
if(ei != null)
{
ei.AddEventHandler(control, new EventHandler((s, e) =>
{
// More awesomeness here...
}));
}
}
So for each string in the event list, get the event from the control and bind a handler.
The problem is that not all events are EventHandler, some for example might be KeyEventHander or MouseEventHandler etc.
I don't want a massive if/else list of EventHandler derived types, I just want to bind the same handler regardless of what type of EventHandler it is.
How can I do this?
Here is one way to do it:
First create this helper class:
public class HandlerHelper<T> where T : EventArgs
{
private readonly EventHandler m_HandlerToCall;
public HandlerHelper(EventHandler handler_to_call)
{
m_HandlerToCall = handler_to_call;
}
public void Handle(object sender, T args)
{
m_HandlerToCall.Invoke(sender, args);
}
}
This generic class has a Handle method that will be used to be our delegate for the many event handler types. Note that this class is generic. T will be one of the many EventArgs derived classes for different event types.
Now let's say you define the following event handler:
var event_handler = new EventHandler((s, args) =>
{
// More awesomeness here...
});
Here is how you can use the helper class to create different delegates for the different event handler types that will invoke event_handler:
foreach (var event_name in event_names)
{
var event_info = control.GetType().GetEvent(event_name);
var event_handler_type = event_info.EventHandlerType;
var event_args_type = event_handler_type.GetMethod("Invoke").GetParameters()[1].ParameterType;
var helper_type = typeof(HandlerHelper<>).MakeGenericType(event_args_type);
var helper = Activator.CreateInstance(helper_type, event_handler);
Delegate my_delegate = Delegate.CreateDelegate(event_handler_type, helper, "Handle");
event_info.AddEventHandler(button, my_delegate);
}
For each event, we obtain the EventHandlerType which is like EventHandler or MouseEventHandler.
Then we use reflection to get the type of the second parameter which is like EventArgs or MouseEventArgs.
Then we create an instance of HandlerHelper<> based on the type of the EventArgs parameter. For example HandlerHelper<EventArgs> or HandlerHelper<MouseEventArgs>.
We give event_handler to the constructor of HandlerHelper so that it can call it when it's Handle method is invoked.
This makes the signature of the Handle method as we want. For example, in the case of HandlerHelper<MouseEventArgs>, the signature of the Handle method is:
void Handle(object sender, MouseEventArgs args)
Now, we use Delegate.CreateDelegate to create a delegate based on the Handle method for the specific HandlerHelper object that we created, and we give such delegate to the AddEventHandler method.
For performance reasons, you can cache the delegate (instead of creating one every time) based on event_args_type.
You can use dynamic key word to attach the handler. More explanation you can find here: https://msdn.microsoft.com/en-us/library/ms228976(v=vs.110).aspx
I am having trouble figuring out how to program delegate method calls across classes in C#. I am coming from the world of Objective-C, which may be confusing me. In Objective-C, I can assign a delegate object inside a child class, to be the parent class (I.e., childViewcontroller.delegate = self;). Then I can to fire a method in the delegate class by using:
if([delegate respondsToSelector:#selector(methodName:)]) {
[delegate methodName:parametersgohere];
}
However, I can't figure out how to do this in C#. I've read a bit about C# delegates in general (for example, here), but I'm still stuck.
Are there any examples that explain this?
Here is my scenario in full:
I have classA which instantiates an instance of classB. ClassB fires a method (which call a web service), and upon response, I'd like to fire a method in classA.
Any 'Hello World' types of tutorials out there that might explain the very basics of this?
A delegate is an object that points to a method, be it a static or instance method. So for your example, you would just use the event model:
class Caller {
public void Call() {
new Callee().DoSomething(this.Callback); // Pass in a delegate of this instance
}
public void Callback() {
Console.WriteLine("Callback called!");
}
}
class Callee {
public void DoSomething(Action callback) {
// Do stuff
callback(); // Call the callback
}
}
...
new Caller().Call(); // Callback called!
The Caller instance passes a delegate to the Callee instance's DoSomething method, which in turn calls the pointed-to method, which is the Callback method of the Caller instance.
In C# what I think you are looking for are called events. They are a language feature that allows a class instance to expose a public delegate in a way that other class instances can subscribe to. Only the exposing class is allowed to raise the event.
In your example:
public class ClassB {
// Note the syntax at the end here- the "(s, e) => { }"
// assigns a no-op listener so that you don't have to
// check the event for null before raising it.
public event EventHandler<MyEventArgs> MyEvent = (s, e) => { }
public void DoMyWork() {
// Do whatever
// Then notify listeners that the event was fired
MyEvent(this, new MyEventArgs(myWorkResult));
}
}
public class ClassA {
public ClassA(ClassB worker) {
// Attach to worker's event
worker.MyEvent += MyEventHandler;
// If you want to detach later, use
// worker.MyEvent -= MyEventHandler;
}
void MyEventHandler(Object sender, MyEventArgs e) {
// This will get fired when B's event is raised
}
}
public class MyEventArgs : EventArgs {
public String MyWorkResult { get; private set; }
public MyEventArgs(String myWorkResult) { MyWorkResult = myWorkResult; }
}
Note that the above will be synchronous. My understanding is that Objective-C delegates are all Actor pattern, so they are asynchronous. To make the above asynch, you'll need to delve into threading (probably want to google "C# Thread pool").
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!
From MSDN:
Any method that matches the delegate's
signature, which consists of the
return type and parameters, can be
assigned to the delegate.
So how is this possible:
public delegate void AlarmEventHandler(object sender, EventArgs e);
public event AlarmEventHandler Alarm;
protected virtual void OnAlarm(EventArgs e)
{
AlarmEventHandler handler = Alarm;
if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
}
delegate AlarmEventHander and event AlarmEventHandler have different signatures yet handler can be assigned to Alarm.
Perhaps I'm mis-understanding delegates somewhat, and I would be very grateful if someone could explain where I'm going wrong.
A delegate is like a class. An event is like a property. When you declare an event in a class, you declare the type of event it is. In this case, AlarmEventHandler, which is an inner class of the top-level class this is a part of.
In the OnAlarm method, you get the instance of the AlarmEventHandler class that has been assigned to the event, and invoke it.
To clear things up, your code above is similar to this, using normal classes & references:
public class InnerClass {
public void MyMethod() { /* ... */ }
}
public InnerClass MyProperty { get; set; }
protected virtual void CallMyMethod() {
InnerClass cls = MyProperty;
if (cls != null)
cls.MyMethod();
}
Actually the signatures are the same. In .NET events are implemented with delegates.
public event AlarmEventHandler Alarm;
So above code is actually compiled by compiler as:
private AlarmEventHandler handler;
public event AlarmEventHandler Alarm
{
add { handler += value; }
remove { handler -= value; }
}
So event actually uses the same AlarmEventHandler delegate.
You are mixing delegates and events. While events are dependant on delegates, they are difference concepts completly. So should be taken separately.
Delegate is like type definition. Place where you use this delegate is like variable or property.
Event makes this delegate behave differently from the outside. Signature is still same.
An event is just a delegate. The delegate itselft can only be accessed from inside the class.
From the outside, only add and remove functionality for the event is possible to you can only do this:
myAlarm.Alarm+=new AlarmEventHandler(callPolice);
// or
myAlarm.Alarm-=new AlarmEventHandler(callPolice);
But from the inside of the class, Alarm is just a delegate of type AlarmEventHandler so you can do what your code shows:
AlarmEventHandler handler = Alarm;
if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
A function which takes a base-class as (non reference) parameter can be converted to a delegate that takes a derived class as parameter. This is because the function that takes base can be safely substituted where-ever a function that takes a derived class is used.
void TakesObject(object o)
{
...
}
Action<string> myAction=TakesObject;
You can call myAction only by passing in a string. And since every string is an object the contract for TakesObject is fulfilled.
In your case is works because every AlarmEventArgs is an EventArgs too. So the contract requirements of your eventhandler are less strict than the contract guarantees of the delegate type used by the event.
This is called co- and contra-variance. You have co-variance in return-types, and contra variance in parameters.
Check this article on MSDN:
Using Variance in Delegates (C# and Visual Basic)
When I try to call the event myevent in the function fire, it gives compiler error as
delegate mydelegate does not take 0 arguments.
if i try to give arguments in calling myevent as
myevent(this);
it again shows error as it does not take 1 parameters. what parameters am i supposed to give in the calling myevent? Here's the program code:
namespace useofdelegates
{
public class Class1
{ int i;
public delegate void mydelegate(object sender,EventArgs e);
public event mydelegate myevent;
public void fire()
{
myevent(this); // *** shows compiler error ***
}
}
}
The compiler error is because you are using a delegate with two parameters and calling it with one.
The simple solution is
myevent(this, EventArgs.Empty);
The more regular (and thread-safe) solution, which I think you're getting confused with is
OnMyEvent(EventArgs.Empty);
// and then later
public void OnMyEvent(EventArgs args)
{
mydelegate handler = myevent;
if(handler != null) handler(this, args);
}
The reason for doing it this way is that you should always make sure that there is at least one handler attached to the event. There is a small chance that the handler can be detached between checking for null and calling the handler, so it's good to pass it on to another variable which it cannot be detached from before checking for null.
You need to pass in 2 parameters. the first one being the source of the event. In your example "this" would work. The second one being of type EventArgs or a realted subclass. You can also pass EventArgs.Empty is you do not wish to pass any event especific information to the event handler.
So your method would look like this
fire(this, EventArgs.Empty);
or
fire(this, new MyCustomEVentArgs());
Here is an MSDN link with an example of using events http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx
and nother one on creating custom EventArg subclasses to use in your handler. http://www.devarticles.com/c/a/C-Sharp/Creating-Custom-Delegates-and-Events-in-C-sharp/
You have declared mydelegate to require an object and an EventArgs, so you have to pass these in. Of course, you don't have declare your delegate with these arguments. You could just declare it thus:
public delegate void mydelegate();
or just pass this as the sender:
public delegate void mydelegate(object sender);
Just create an empty EventArgs I guess.
myevent(this, new EventArgs());
Note, here is the full code:
namespace useofdelegates
{
public class Class1
{
int i;
public delegate void mydelegate(object sender, EventArgs e);
public event mydelegate myevent;
public void fire()
{
myevent(this, new EventArgs()); // *** shows compiler error ***
}
}
}