Get event name from Action<T> - c#

I would like to be able to do that:
Verify.EventIsHooked(a => a.Event1 += null);
Having this scenario:
class A
{
public event EventHandler Event1;
}
class static Verify
{
public static void EventIsHooked<T>(Action<T> action)
{
// What comes here to extract the event name?
}
}
How can I extract the Event1 name from the expression?
Thanks
PD: I know how to check if an event is hooked or not. I just need to extract the event name.
Edit:
The idea behind is to be able to get the delegates attached to an event in order to determine if an event has been hooked/unhooked in a unit test, without passing the event name as string to the reflection call.
Moq uses lambdas like a => a.Event1 += null when raising events, since it looks like the only way to pass an event in an expression.

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;

C# has to subscribe to an event before event called

I want to implement a fast way to add listener to an event, my implementation:
public class AccountManager
{
public delegate void CheckIfLoggedInListener(EventArgs e);
public event CheckIfLoggedInListener SetCheckIfLoggedInListener;
public void CheckIfLoggedIn()
{
if(SetCheckIfLoggedInListener!=null)
SetCheckIfLoggedInListener(new EventArgs("e"));
}
}
Right now, I have to set listener first, then invoke the method, which can be easily messed up if other developer doesn't pay attention:
//this will not work, because you invoke the event before subscribing
accountManager.CheckIfLoggedIn();
accountManager.SetCheckIfLoggedInListener += (e) => { Debug.Log(e.param); };
I wonder if there is a way to make the order not mandatory?
You can take a parameter of generic delegate that will be object of type Action<T> and in your case it would beAction<EventArgs> in your method to make sure that:
EDIT:
you would need to tweak it to work, instead of Action<EventArgs>, we would have to use Action<Object,EventArgs>, this post explains why is that
public void CheckIfLoggedIn(Action<object,EventArgs> action)
{
SetCheckIfLoggedInListener = action.Invoke;
if(SetCheckIfLoggedInListener!=null)
SetCheckIfLoggedInListener(null,new EventArgs());
}
and when calling you would need to do like:
//this will not work, because you invoke the event before subscribing
accountManager.CheckIfLoggedIn((o,e) => { Console.WriteLine("event fired"); });
Working DEMO Fiddle here
This way you can make it mandatory for user to register the implementation to be called when event is fired.
Hope it helps!

Determine event name from EventHandler<T> in C#

My question is if I can determine an event's name passed through a parameter with EventHandler type? With some code it becomes more clear:
public void RaiseEvent<T>(EventHandler<T> eventToRaise, T args) where T : EventArgs, IXmlConvertable
{
Log(eventToRaise.Method.Name, args.ToXElement());
ThreadPool.QueueUserWorkItem((e) => eventToRaise(this, args));
}
public event EventHandler<ProductLeftEventArgs> ProductLeftEvent = delegate { };
As you can see I want to create a method that not only calls the event async, but also logs the action. IXmlConvertable is a custom interface, which is used for logging. I call the method like this:
this.RaiseEvent(this.ProductLeftEvent, new ProductLeftEventArgs() { ... });
What I would like to gain is a string with "ProductLeftEvent". Unfortunately the eventToRaise.Method.Name gives the string "DeliverEvent". Do you think it is possible to achieve this name? It also worth mention, that I use weak event manager when I assing a handler.
eventToRise parameter is a delegate - i.e. reference to a method (and a target or object on which this method should be called). Event holds a reference to all methods that were subscribed to that event.
So in your case it seems that "DeliverEvent" is the method that was attached to your event in some other part of the application like this: someObject.ProductLeftEvent += this.DeliverEvent;
If you really want to pass event itself to the RaiseEvent method you either need to use expressions or simply pass an eventName (but this will be a kind of duplication).
Below is an example of how to use expressions to do this:
public void RaiseEvent<T>(Expression<Func<EventHandler<T>>> eventToRaise, T args) where T : EventArgs, IXmlConvertable
{
string eventName = ((MemberExpression)eventToRaise.Body).Member.Name;
Log(eventName, args.ToXElement());
EventHandler<T> eventHandler = eventToRaise.Compile()();
ThreadPool.QueueUserWorkItem((e) => eventHandler(this, args));
}
You will call this method like this:
this.RaiseEvent(() => this.ProductLeftEvent, new ProductLeftEventArgs() { ... });

C# Create a Delegate that fires an event?

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() { ... }

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.
:)

Categories

Resources