I am looking to pass an event to a helper function. This function will attach a method to the event. However, I am having trouble properly passing the event. I have tried passing a EventHandler<TEventArgs>. It compiles, but events are not attached (but are still added; it seems a copy of the event handler is made).
For example, if I have this:
public event EventHandler<EventArgs> MyEvent;
And the helper function:
public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo)
{
eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); };
}
And the caller:
MyHelperFunction(MyEvent);
MyEvent(null, new EventArgs()); // Does nothing.
The reason why this does not work is += when applied to a delegate creates a new delegate which is the combination of the old and the new. It does not modify the existing delegate.
In order to get this to work you will have to pass the delegate by reference.
public static void Helper(ref EventHandler<EventArgs> e)
{
e+= (x,y) => {};
}
The reason this works outside of the method is because the LHS is still the actual field. So += will create a new delegate and assign back to the member field.
Just came up with this little helper. If it is your self-created Event you could use a wrapper like this. You can use your += operators to attach handlers as normal but can pass the wrapper around and even raise the event from elsewhere.
public class GenericEvent<T> where T:EventArgs
{
public event EventHandler<T> Source = delegate { };
public void Raise(object sender, T arg = default(T))
{
Source(sender, arg);
}
public void Raise(T arg = default(T))
{
Source(this, arg);
}
public void AddHandler(EventHandler<T> handler)
{
Source += handler;
}
public void RemoveHandler(EventHandler<T> handler)
{
Source -= handler;
}
public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler)
{
genericEvent.AddHandler(handler);
return genericEvent;
}
}
Create the event like:
public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>();
Attach handlers:
MyEvent += (s,e) => {};
Raise event:
MyEvent.Raise();
Just guessing: Have you tried passing it as ref?
public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo)
MyHelperFunction(ref MyEvent);
It's not exactly nice, but you can use reflection to do this.
public EventMonitor(object eventObject, string eventName)
{
_eventObject = eventObject;
_waitEvent = eventObject.GetType().GetEvent(eventName);
_handler = new EventHandler(SetEvent);
_waitEvent.AddEventHandler(eventObject, _handler);
}
Where eventObject is the object containing the event, and eventName is the name of the event.
SetEvent is your event handler.
I also have a dispose method like this:
public void Dispose()
{
_waitEvent.RemoveEventHandler(_eventObject, _handler);
}
I have a solution where I have an two interfaces. The first interface has methods for binding certain events, while the other interface has event methods that can be bound to those events.
The first interface's bind methods takes the second interface as parameter, which makes it possible to bind the events to the event methods of any class that implements the second interface.
Is that understandable, or would you prefer some code? :)
As many have pointed out, passing an event to a method is either not possible or not simple.
Please clarify, but I suspect your intended usage will look something like:
void Register()
{
var super = new SuperHandler();
//not valid syntax:
super.HandleEvent(MyEvent1);
super.HandleEvent(MyEvent2);
super.HandleEvent(MyEvent3);
super.HandleEvent(MyEvent4);
}
You can accomplish this simply by making your intended generic event handlers accessible publicly (or internally, if you desire):
public static class GenericHandler
{
public static void HandleAnyEvent(object sender, EventArgs e)
{
//handle
}
}
public class SomeClass
{
void RegisterEvents()
{
var r = new EventRaiser();
r.ImportantThingHappened += GenericHandler.HandleAnyEvent;
}
}
In this example my catch-all handler is in a static class, but you can just as well use a non-static class. Also, I see that in your example you have made the method generic (TEventArgs). Because all EventHandler derivatives (such as CancelEventHandler) match the base EventHandler, you do not need to involve generics (nor would it be helpful).
If the registration logic is complex or you must keep the EventHandler private, consider using Interface Events. This may not meet your intended goal of reducing the amount of code, but it will allow you to create a class that can predictably handle all of the events of a specific type.
interface IRaiseEvents
{
event EventHandler ConnectionCreated;
event EventHandler ConnectionLost;
}
public class SuperHandler
{
void RegisterEvents(IRaiseEvents raiser)
{
raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected.");
raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected.");
}
}
Pass something like Action e = e => myevent += e;
And call from method with the handler? It has the benefit of working with .NET classes.
Related
I'm struggling to understand why it's okay to attach a 'normal' method as a subscriber to a publisher event, and also a delegate.
For example...
public class Caller
{
public string Name { get; set; }
public event EventHandler<RuedaEventArgs> MakeRuedaCall;
public virtual void OnMakeRuedaCall(RuedaEventArgs args) {
if (MakeRuedaCall != null) {
MakeRuedaCall(this, args);
}
}
}
This is my publisher class where I define and raise the event. I'm also making use of some custom event arguments.
public class Salsera {
public Salsera(Caller caller) {
caller.MakeRuedaCall += MakeMovement;
}
public void MakeMovement(object source, RuedaEventArgs args) {
if (args.CallName == "Vacilala") {
Turn();
}
if (args.CallName == "Patin") {
MoveToOutside();
}
}
private void MoveToOutside() {
Console.WriteLine("Ladies move to the outside....");
}
private void Turn() {
Console.WriteLine("Ladies turn....");
}
}
This is a class where I add a method as a subscriber to the event in the constructor.
Suppose I then have somewhere else...
Caller matt = new Caller();
EventHandler<RuedaEventArgs> anonyMouseFunc = (sender, eventArgs) =>
{
switch (eventArgs.CallName) {
case "Patin":
Console.WriteLine("Adding a new subscriber for Patin");
break;
case "Vacilala":
Console.WriteLine("Adding a new subscriber for Vacilala");
break;
}
};
matt.MakeRuedaCall += anonyMouseFunc;
Sorry if this seems like a silly question but why is it that you can subscribe a 'normal' method (assuming it matches the delegate signiture) to an event, and also an anonymous method as a delegate to an event.
i.e. how does public event EventHandler<RuedaEventArgs> MakeRuedaCall; handle both options?
Many thanks,
Sorry if this seems like a silly question but why is it that you can subscribe a 'normal' method (assuming it matches the delegate signiture) to an event, and also an anonymous method as a delegate to an event.
You're subscribing a delegate in both cases. In this case:
caller.MakeRuedaCall += MakeMovement;
... you're using a method group conversion to convert MakeMovement (which is a method group in spec terminology) into a delegate instance. That code is (almost entirely) equivalent to:
caller.MakeRuedaCall += new EventHandler<RuedaEventArgs>(MakeMovement);
Or to think of it another way, it's equivalent to:
EventHandler<RuedaEventArgs> handler = MakeMovement;
caller.MakeRuedaCall += handler;
This ability to create delegates from regular methods isn't just for event handling, of course - you can use it for LINQ and anywhere else you use delegates, too.
I am completely new to events in c# and this is what I want to do:
I have two methods:
OpenPage1();
OpenPage2();
When either of these methods is called, I want a third method named as PerformSomeTask() to be called.
I believe that this can be done by event handling. Could anyone tell me how to do this?
All you have to do in your class is to add an event with a correct eventHandler (Action in your example). The eventHandler should correspond the method that will subscribe to this event.
Then you fire the event from the openPage Methods.
You must check for null in case no one subscribed to this event.
public class Foo
{
public event Action theEvent;
public void OpenPage1()
{
if (theEvent != null)
theEvent();
}
public void OpenPage2()
{
if (theEvent != null)
theEvent();
}
}
public class Bar
{
public int Counter { get; set; }
public void PerformSomeTask()
{
Counter++;
}
}
And here's a test that you can run to see it all together:
[TestMethod]
public void TestMethod1()
{
var foo = new Foo();
var bar = new Bar();
foo.theEvent += bar.PerformSomeTask;
foo.OpenPage1();
foo.OpenPage2();
Assert.AreEqual(2, bar.Counter);
}
Events is a big part of C#.
To be simple, you need first a delegate that describe type of called method. In your example, PerformSomeTask is void and take no parameters.
So declare in your class
public delegate void PerformSomeTask();
Then, you need to declare event, which is the member that will called to launch your function
public event PerformSomeTask OnPerformSomeTask;
On your both methods, OpenPage1 and OpenPage2, you need to check if someone subscribe to your event, if yes, call it.
if(OnPerformSomeTask != null)
OnPerformSomeTask();
This will launch every method that subscribe to your event. Subscribers can be multiple.
To subscribe, just do it like this :
YourClass.OnPerformSomeTask += MyVoidMethod;
[...]
public void MyVoidMethod() { DoSomething(); [...] }
Your void method will be called everytime your run OpenPage1 and OpenPage2
If you need some parameters, juste change your delegate to proceed.
public delegate void PerformSomeTask(string myParam);
Then, your methods will have this parameter as standard function parameter (call your event with your value to pass it as parameter to every subscriber function).
Is it possible to assign the same method to multiple delegates all at once?
public class Hoge
{
public event Action EventA;
public event Action EventB;
public event Action EventC;
public Hoge()
{
EventA += () => FugaMethod();
EventB += () => FugaMethod();
EventC += () => FugaMethod();
}
private void FugaMethod()
{
Console.WriteLine("Hello.");
}
}
I'd like to simplify the assignments of the FugaMethod().
since events can't go as a parameter ... unfortunately ... no
if we are talking about a shitload of events, reflection would be the way to go ... but that i'd not really call "simplification"
edit:
for clarification:
what you can pass as a parameter:
the current list of eventhandlers attached to a static or specific event on a specific object (what you pass in this case is not the event, but a MulticastDelegate)
what you can not pass as a parameter: the event itself ...
you are not able to pass an event in a direct way that would allow to attach another event handler ... for that you would probably want to pass the specific obect instance, the EventInfo describing the event itself, and the new handler(s) ...
regarding "simplification":
what you need to do would be:
-use reflection to obtain the EventInfo objects of the desired events
-for each instance and each EventInfo call EventInfo.AddEventHandler, passing the instance as target and the eventhandler as handler
since you cannot pass the event as a parameter, you cannot extract a simple and typesafe method to get the desired EventInfo objects. you will have to use some selection by name, or other logic that takes the instance type apart by using Type.getEvent or Type.getEvents
so, if you are not handling a shitload of events, like a few hundred, writing it like you did seems to be the preferable way to go ... the reflection approach will be anything but simpler or shorter
There is a way to assign multiple event handlers at once that does not require reflection however itäs not trivial and some programming is necessary. You can use a dictionary to store your events if you want initialize them in a loop.
As a side note: by convention the event keyword should only be used if the delegate is of type EventHandler. It may and probably will confuse others when they try to use it.
Hoge class:
public class Hoge
{
// A dictionary to store your events.
private Dictionary<string, EventHandler> events = new Dictionary<string, EventHandler>()
{
{ "EventA", null },
{ "EventB", null },
{ "EventC", null }
};
// Event add/remove accessors.
public event EventHandler EventA
{
add
{
lock (events)
{
events["EventA"] += (EventHandler)events["EventA"] + value;
}
}
remove
{
lock (events)
{
events["EventA"] += (EventHandler)events["EventA"] - value;
}
}
}
// You can do the same for other events.
public event EventHandler EventB;
public event EventHandler EventC;
public Hoge()
{
// Initialize events in a loop.
foreach (var key in events.Keys.ToList())
{
events[key] += FugaMethod;
}
}
// Raises EventA.
public void RaiseEventA()
{
EventHandler handler;
if (null != (handler = (EventHandler)events["EventA"]))
{
handler(this, EventArgs.Empty);
}
}
// Event handler.
private void FugaMethod(object sender, EventArgs e)
{
Console.WriteLine("Hello.");
}
}
Usage:
class Program
{
static void Main(string[] args)
{
new Hoge().RaiseEventA();
}
}
How to: Use a Dictionary to Store Event Instances (C# Programming Guide)
Handling and Raising Events
I have a usercontrol that raises an event after communicating with a web service. The parent handles this event when raised. What I thought would be the proper approach would be to pass the object returned from the webservice to the parent as eventargs???
If this is the proper way I can't seem to find the instructions on how to do so.
UserControl
public event EventHandler LoginCompleted;
then later after the service returns biz object:
if (this.LoginCompleted != null)
{
this.LoginCompleted(this, new EventArgs() //this is where I would attach / pass my biz object no?);
}
Parent
ctrl_Login.LoginCompleted += ctrl_Login_LoginCompleted;
....snip....
void ctrl_Login_LoginCompleted(object sender, EventArgs e)
{
//get my object returned by login
}
So my question is what would be the "approved" method for getting the user object back to the parent? Create a property class that everything can access and put it there?
You would have to declare your event using EventHandler<T> where T is your class that derives from EventArgs:
public event EventHandler<LoginCompletedEventArgs> LoginCompleted;
LoginCompletedEventArgs could look like this:
public class LoginCompletedEventArgs : EventArgs
{
private readonly YourBusinessObject _businessObject;
public LoginCompletedEventArgs(YourBusinessObject businessObject)
{
_businessObject = businessObject;
}
public YourBusinessObject BusinessObject
{
get { return _businessObject; }
}
}
Usage would be like this:
private void RaiseLoginCompleted(YourBusinessObject businessObject)
{
var handler = LoginCompleted;
if(handler == null)
return;
handler(this, new LoginCompletedEventArgs(businessObject));
}
Please notice how I implemented RaiseLoginCompleted. This is a thread-safe version of raising the event. I eliminates a possible NullReferenceException that can occur in a race condition scenario where one thread wants to raise the event and another thread un-subscribes the last handler after the if check but before actually invoking the handler.
Well, you could do all that or you could define a delegate as your EventHandler and define your properties in its signature.
Such as:
public delegate void MyEventEventHandler(int prop1, string prop2, object prop3...);
public event MyEventEventHandler MyEvent;
I recommend use named tuples with EventHandler<TEventArgs>.
I like olddog's answer. Microsoft already has this delegate EventHandler< TEventArgs >.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
You don't need to inherits from EventArgs.
Declare your event handler with named tuples.
public event EventHandler<(int id, string message, object LoginObject)> LoginCompleted;
In your client code, assign method to the LoginCompleted event handler
option 1: use lambda
LoginCompleted += (o, e) =>
{
Console.WriteLine($"Hello, sender is {o.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
};
option 2: call a local method
LoginCompleted += onLoginCompleted;
private static void onLoginCompleted (object sender, (int id, string message, object LoginObject) e)
{
Console.WriteLine($"Hello, sender is {sender.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
}
I just wrote an example, please refer to my repo
I personally like Toni Petrina's approach (see https://coderwall.com/p/wkzizq/generic-eventargs-class). It differs from the accepted answer in that you don't have to create a special EventHandler class (e.g. LoginCompletedEventArgs).
(Note: I am using VS 2015 and C# v6. In older versions of Visual Studio and C#, you may have to add using System.Linq;)
Create a generic EventArgs<T> class that inherits from EventArgs...
class EventArgs<T> : EventArgs {
public T Value { get; private set; }
public EventArgs(T val) {
Value = val;
}
}
Declare your event handler...
public event EventHandler<EventArgs<object>> LoginCompleted;
Assuming you have declared and assigned an object named loginObject, add code to raise you event...
private void RaiseLoginCompleted() {
if (LoginCompleted != null)
LoginCompleted(this, new EventArgs<object>(loginObject));
}
In your client code, add the LoginCompleted event handler (uses Linq and calls a local method)...
LoginCompleted += (o, e) => onLoginCompleted(e.Value); // calls a local method
void onLoginCompleted(LoginObject obj) {
// add your code here
}
sometimes it sucks to create a class for merely passing a bool as a derived EventArgs! so you can simply use Action instead of EventHandler. you can pass any type and how many parameters you like (Action supports Up to 16).
class Raiser
{
public event Action<Raiser, bool,DateTimeOffset> OnCreate;
public void Create()
{
OnCreate?.Invoke(this, true,DateTimeOffset.Now);
}
}
class Listener
{
Raiser raiser;
public Listener()
{
raiser = new Raiser();
raiser.OnCreate += Raiser_OnCreate;
}
private void Raiser_OnCreate(Raiser arg1, bool arg2,DateTimeOffset dateTimeOffset)
{
throw new NotImplementedException();//Do Your works here
}
}
generally using Action and 'Func' are easier than Delegate.
I'm using C#, .NET 3.5. I understand how to utilize events, how to declare them in my class, how to hook them from somewhere else, etc. A contrived example:
public class MyList
{
private List<string> m_Strings = new List<string>();
public EventHandler<EventArgs> ElementAddedEvent;
public void Add(string value)
{
m_Strings.Add(value);
if (ElementAddedEvent != null)
ElementAddedEvent(value, EventArgs.Empty);
}
}
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
MyList tmp = new MyList();
tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired);
tmp.Add("test");
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
However, what I do not understand, is when one declares an event handler
public EventHandler<EventArgs> ElementAddedEvent;
It's never initialized - so what, exactly, is ElementAddedEvent? What does it point to? The following won't work, because the EventHandler is never initialized:
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
EventHandler<EventArgs> somethingHappend;
somethingHappend += new EventHandler<EventArgs>(Fired);
somethingHappend(this, EventArgs.Empty);
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
I notice that there is an EventHandler.CreateDelegate(...), but all the method signatures suggest this is only used for attaching Delegates to an already existing EventHandler through the typical ElementAddedEvent += new EventHandler(MyMethod).
I'm not sure if what I am trying to do will help... but ultimately I'd like to come up with an abstract parent DataContext in LINQ whose children can register which table Types they want "observed" so I can have events such as BeforeUpdate and AfterUpdate, but specific to types. Something like this:
public class BaseDataContext : DataContext
{
private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>();
public static void Observe(Type type)
{
if (m_ObservedTypes.ContainsKey(type) == false)
{
m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>());
EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler);
}
}
public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events
{
get { return m_ObservedTypes; }
}
}
public class MyClass
{
public MyClass()
{
BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);
}
public void OnUserUpdated(object sender, EventArgs args)
{
// do something
}
}
Thinking about this made me realize I don't really understand what's happening under the hod with events - and I would like to understand :)
I've written this up in a fair amount of detail in an article, but here's the summary, assuming you're reasonably happy with delegates themselves:
An event is just an "add" method and a "remove" method, in the same way that a property is really just a "get" method and a "set" method. (In fact, the CLI allows a "raise/fire" method as well, but C# never generates this.) Metadata describes the event with references to the methods.
When you declare a field-like event (like your ElementAddedEvent) the compiler generates the methods and a private field (of the same type as the delegate). Within the class, when you refer to ElementAddedEvent you're referring to the field. Outside the class, you're referring to the field.
When anyone subscribes to an event (with the += operator) that calls the add method. When they unsubscribe (with the -= operator) that calls the remove.
For field-like events, there's some synchronization but otherwise the add/remove just call Delegate.Combine/Remove to change the value of the auto-generated field. Both of these operations assign to the backing field - remember that delegates are immutable. In other words, the autogenerated code is very much like this:
// Backing field
// The underscores just make it simpler to see what's going on here.
// In the rest of your source code for this class, if you refer to
// ElementAddedEvent, you're really referring to this field.
private EventHandler<EventArgs> __ElementAddedEvent;
// Actual event
public EventHandler<EventArgs> ElementAddedEvent
{
add
{
lock(this)
{
// Equivalent to __ElementAddedEvent += value;
__ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value);
}
}
remove
{
lock(this)
{
// Equivalent to __ElementAddedEvent -= value;
__ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value);
}
}
}
The initial value of the generated field in your case is null - and it will always become null again if all subscribers are removed, as that is the behaviour of Delegate.Remove.
If you want a "no-op" handler to subscribe to your event, so as to avoid the nullity check, you can do:
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
The delegate {} is just an anonymous method which doesn't care about its parameters and does nothing.
If there's anything that's still unclear, please ask and I'll try to help!
Under the hood, events are just delegates with special calling conventions. (For example, you don't have to check for nullity before raising an event.)
In pseudocode, Event.Invoke() breaks down like this:
If Event Has Listeners
Call each listener synchronously on this thread in arbitrary order.
Since events are multicast, they will have zero or more listeners, held in a collection. The CLR will loop through them, calling each in an arbitrary order.
One big caveat to remember is that event handlers execute in the same thread as the event is raised in. It's a common mental error to think of them as spawning a new thread. They do not.