C# Create a Delegate that fires an event? - c#

Is it possible to use Reflection is C# to fire an event? Say I have a situation like this:
public delegate void SomeEventHandler(object sender, BenArgs e);
class EventHub
{
public event SomeEventHandler SOME_EVENT;
public void fireEvent(String eventName)
{
SomeEventHandler evt = (SomeEventHandler) Delegate.CreateDelegate(typeof(SomeEventHandler), this, eventName);
evt.Invoke(null, null);
}
}
Meaning if I call
EventHub.fireEvent("SOME_EVENT")
then it makes the event hub fire SOME_EVENT? I've tried this and am just getting exceptions.
This is mostly a curiosity of mine, I know that in this case I could just as easily fire the event without reflection.

Assuming your current scenario, i.e.:
A field-like event.
The backing delegate-field has the same name as the event.
(this, EventArgs.Empty) are valid arguments to pass to the delegate.
You can do something like this (more argument validation required):
public void fireEvent(String eventName)
{
// Get a reference to the backing field
var del = (Delegate)typeof(EventHub)
.GetField(eventName, BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(this);
// Invoke the delegate, it's invocation-list will contain the listeners
if(del != null)
del.DynamicInvoke(this, EventArgs.Empty);
}
Usage:
var eHub = new EventHub();
eHub.SOME_EVENT += delegate { Console.WriteLine("SOME_EVENT fired.."); };
eHub.fireEvent("SOME_EVENT");
Now, you can generalize this idea with an extension method on object if you like, but all of this is a really bad idea. This problem can't be solved in the general case because one can't know how an event is "implemented." There could be arbitrary code inside the add and remove methods, and the logic to "fire" the event could also be just about anything. There might not even be a backing multicast delgate field to store the listeners.
In any case, trying to tell an object to fire an event is almost always a sign of a major design-flaw, IMO. If you still really do want this, it would be much better to declare a method like this in your class:
public void RaiseXXXEvent() { ... }

Related

Can I have only event without delegate?

I have a player with the event "shoot". If the player shoots a bullet I manually trigger the event. A different GameObject has a script where if listener and if event triggered, listener perform specific method. I'm new to events. I really don't need delegate, only event. I'm teaching myself programming from internet but everywhere is how to use event only with delegate.
Can I have only event without delegate and if yes how to declare event like this?
An event is a delegate with safety constraint.
You can only call an event from the class that holds it.
You can only register/unregister to an event from outside the class (only +=/-=)
You cannot pass an event as parameter
You can only wipe clean an event from the class that holds it (eventName = null)
Off topic : UnityEvent is not a real event, it is a class that contain a list of delegate (Action).
When you call an event (or a delegate), you should always check for nullity as you never know if the reference points to an object. You do not need this with UnityEvent since as I mentioned it is not real event and then if the collection is empty nothing happens.
public delegate void MyDel();
// The two following behaves the same
public MyDel myDel;
public Action myAction;
public event Action myEvent;
void Start(){
if(myAction != null){ myAction(); }
if(myEvent != null) { myEvent(); }
if(myEvent != null) { myEvent.Invoke(); }
}
those are same things, but the choice of whether one or the other is based on what you want to do.
Consider the following in another class:
void Start(){
refToClass.myAction = MyMethod;
refToClass.myEvent += MyMethod;
}
void MyMethod(){}
First case will remove all methods attached to the delegate and MyMethod is then the only one left to listen.
The event will not allow that, only +=/-= are allowed so you can only remove/add a method without affecting the others.
A delegate basically describes a function type. An event is a hook where registered functions matching a given function type are called when the event occurs.
Putting together these two simplified definitions, a delegate is also a descriptor for an event, therefore you cannot have an event without a related delegate.
Anyway, you are not forced to declare a new delegate for every event you declare. Instead, you can use any of the preexisting delegates such as EventHandler, EventHandler<TEventArgs> or even any of the Action overloads, choosing in the one that fits you best in each case.
I believe you technically can have events without a delegate as described in the answers here.
However, delegates are used to pass information from where the event was raised. Without it, the program doesn't know where the event was called from and what information to pass.
This is a good introduction and there are lots of examples on SO to help with niche cases. For now, if you're self-learning programming, I'd recommend you learn the pattern and get it working before worrying about why the delegate and event always come as a pair.
public delegate void ShootEventHandler(object sender, ShootEventArgs e);
public class ShootEventArgs
{
public ShootEventArgs(string s) { Text = s; }
public String Text {get; private set;} // readonly
}
It's commmon to pass this in sender and then new up the delegate you created. Where you want to call shoot, you'd have something like this.
var handler = this.Shoot; // Get the event you want to fire
handler?.Invoke(this, new ShootEventArgs("Bang")); // tell eveything interested "Bang"
GameObjects interested in the shoot event can subscribe to the information like so:
this.Player.Shoot += this.ShootHandler;
public void ShootHandler(object sender, ShootEventArgs e) // common to use e for event args
{
Console.WriteLine(e.Text); // print "Bang"
}
Yes, you can declare an event without declaring a delegate by using Action. Action is in the System namespace. So make sure you add "using System;" statement at the top of your code file.
//No parameter
delegate void _delegate1();
static event _delegate1 _event1;
//Parameterized
delegate void _delegate2(bool flag);
static event _delegate2 _event2;
this is equivalent to
static event Action _event1;
static event Action<bool> _event2;

Events: Get Event Observer from Observed

I'm not sure how to get a collection of the observers of an event from the observed class, when using events and delegates.
http://msdn.microsoft.com/en-us/library/aa645739%28v=vs.71%29.aspx
In this example an instance of EventListener is subscribed to an instance of ListWithChangedEvent. Using the vanilla observer pattern it would be easy to get a collection objects that are listening to ListWithChangedEvent (as it has to maintain a collection of observers anyway). However, using events, its a bit less clear how to produce this collection within ListWithChanged of the observer objects.
Is it something like:
Changed.GetInvocationList().Select(item => item.Target).OfType<EventListener>().ToList();
Seems doubtful, it seems a bit complex.
Edit: This compiles and gives me my answer. I'm just a bit wary that its a bad way to do it.
You're just about there, you just have to remember that you can't use GetInvocationList() on delegates marked with event from outside the declaring class, so you need to either declare a backing field or make the call from within the class.
class EventListener
{
public EventPublisher publisher = new EventPublisher();
public EventListener()
{
publisher.Event += HandleEvent;
}
void HandleEvent(object sender, EventArgs e)
{
}
}
class EventPublisher
{
public event EventHandler Event;
public List<EventListener> GetListeners()
{
return Event.GetInvocationList().Select(i => i.Target).OfType<EventListener>().ToList();
}
}
EDIT: If you're dealing with a 3rd party class, so you can't use a backing field or call GetInvocationList() within the class, then you can use reflection:
var fieldInfo = typeof(EventPublisher).GetField("Event", BindingFlags.Instance | BindingFlags.NonPublic);
var eventHandler = (EventHandler)fieldInfo.GetValue(listener.publisher);
var listeners = eventHandler.GetInvocationList().Select(i => i.Target).ToList();

Additional functionality Events provide over delegate

As I understand an Event is a way for a class to allow clients to give it delegates to methods that should be called when the event occurs. When the event occurs, the delegate(s) given to it by its clients are invoked.
But as demonstrated in following code above said functionality can also be achieved by delegate only i.e. without using delegate.
class Program
{
static void Main(string[] args)
{
ListWithChangedEvent lwce = new ListWithChangedEvent();
lwce.delegateVariable = DelegateTestMethod;
lwce.Add("test");
Console.ReadLine();
}
public static void DelegateTestMethod(object sender, object e)
{
}
}
public delegate void ChangedEventHandler(object sender, object e);
public class ListWithChangedEvent : System.Collections.ArrayList
{
public override int Add(object value)
{
int result = base.Add(value);
if (delegateVariable != null)
delegateVariable(this, "");
return result;
}
public ChangedEventHandler delegateVariable;
}
So, I was wondering what additional functionality does Events provide?
So, I was wondering what additional functionality does Events provide?
Events provide two distinctly different advantages over exposing a public delegate:
You're making the intent very clear. A delegate is typically exposed publically for a very different purpose than an "event" - by using an event, you're very clearly saying "this is something that will get raised at a specific point". Exposing a delegate typically has a different meaning - most often a delegate in a public API is a required input for that API - ie: something that is used directly by the method, not an optional notification mechanism triggered by the method.
Events, technically, are not necessarily just a delegate. An event actually has the option of allowing custom add and remove accessors, which allow you to manually determine what happens when a subscriber subscribes or unsubscribes from the event. For example, many implementations of ICommand.CanExecuteChanged actually don't include their own delegate at all - but silently route to the CommandManager's RequerySuggested event.
Your example allows for a single delegate to be called. The event is a collection of delegates, meaning you can += and -= your heart away (even during event invocation).
event is just the access approach to the handler.
it wont allow you to do myHandler=myFunc;
only using += ( from outer class)
it was made that if another dumb use your code - so he wont destroy your chain by using = so you allow him only += or -=

C# Internal delegate and public Event

I'm currently developing a tiny technical Framework that is independant of any applications. Business code just refers to this Framework.
According this article : http://msdn.microsoft.com/en-us/library/5z57dxz2.aspx (exemple 2), we need to provide a delegate for the custom event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code and that isn't logical for me, so what is the best way to raise a custom Event with a delegate that is only "internal" and not "public" ?
Thanks for help.
I am not sure if I get it right or not. I think that you feel like if you provide a public Delegate type for your custom event, anyone will be able to Raise that event.
Well, that is not true. Only the class that defines that custom event can raise it. If this is your issue, don't worry.
Not true. It's not allowed to invoke an event outside the class which the event belongs to. Others can only use += and -= operators to your event. Only in the class, you can invoke the event. That is a difference between an event and a normal delegate. That is:
public Data
{
public event EventHandler OnSave
public EventHandler OnLoad;
private void Load()
{
if (OnLoad!=null) OnLoad();
//other operations
}
private void Save()
{
if (OnSave!=null) OnSave();
//other operations
}
}
And outside the class:
Data data = new Data();
data.OnLoad += (s,e) => {};
data.OnSave += (s,e) => {};
data.OnLoad = (s,e)=>{};
//data.OnSave = (s,e)=>{}; //invalid
data.OnLoad();
//data.OnSave(); //invalid
The delegate is just a type declaration describing the "signature" of your event. This has to be public. To actually invoke your event you often implement a method named OnEvent (where you substitute Event with Click or Closed or whatever describes your event). This method should be private (or protected) in your class.
You cannot declare an event using a delegate that is less "visible" than the event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code
That isn't true. You declare an event as follows:
public event FooEventHandler Foo;
The only thing that external code can do with the event is subscribe to it (+=), or unsubscribe from it (-=). It can't access the actual delegate, which is generated by the compiler as a private field. In other words, this code would be invalid :
SomeClass x = new SomeClass();
x.Foo(x, new FooEventArgs()); // compilation error here
Don't forget that an event is actually a pair of methods (add and remove). The compiler rewrites the event declaration to something along those lines:
private FooEventHandler _foo;
public event FooEventHandler Foo
{
add { _foo += value; }
remove { _foo -= value; }
}
(the generated code is actually a bit more complex, with some locking to ensure thread safety)
As you can see, the _foo field is private, so client code can't access it. Only the event's add and remove accessors are accessible.
One way of doing it:
Instead of public event, create a method that will manually subscribe your desired delegates, and store them in `private List _delegates' field.
Then, from the 'inside', call each of them when you desire.
public class Framework
{
public delegate void Method();
public void AttachEvent(Method M)
{
_methods.Add(M);
}
private List<Method> _methods;
private FireMethods()
{
_methods.Foreach(x=>x.Invoke());
}
}
Or, you can embrace 'by design' feature of the events that they aren't publicly Invoke()-able.
:)

How do I get the subscribers of an event?

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

Categories

Resources