Adding an action to event handler arguments c# - c#

I have a problem with some code I need to refactor. Right now it uses lambdas as event handlers, but they are not removed properly. From what I have read, this is not even possible? Anyway I would like to rewrite it to use a delegate instead of an anonymous function, and now my problem is that right now it takes an action as parameter, and I can't seem to figure out how to pass the action on to my new delegate. This is the code:
void RetrieveData(
int pointId,
int? chartCollectionId,
Action action)
{
if (pointId <= 0)
throw new ArgumentException("PointId not valid");
LastPointId = NextPointId;
NextPointId = pointId;
Clear();
_csr = new CustomerServiceRepository();
_csr.ServiceClient.GetChartDataCompleted += (se, ea) =>
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr = null;
};
_csr.ServiceClient.GetChartDataAsync(
Settings.Current.Customer.CustomerName,
pointId,
chartCollectionId);
_csr.ServiceClient.GetChartDataCompleted -= (se, ea) => //remove after usage
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr = null;
};
}
I was thinking that maybe I could create the following:
public class extendedEventArgs : GetChartDataCompletedEventArgs
{
Action foo { get; set; }
}
void tang(object sender, extendedEventArgs e)
{
_cachedCharts = e.Result;
ChartDataRetrieved(e.Result);
if (action != null)
action.Invoke();
_csr = null;
}
And the pass the action as a parameter in the extended event args, but when I try to use it like this
_csr.ServiceClient.GetChartDataCompleted += new EventHandler<extendedEventHandler>(tang);
It gives an error:
Cannot implicitly convert type System.EventHandler<Conwx.Net.Client.CustomerClient.Controls.ChartControls.ChartListForecast.extendedEventArgs>' to System.EventHandler<Conwx.Net.Client.Framework.CustomerServiceReference.GetChartDataCompletedEventArgs>'
What am I doing wrong here? Alternative solutions are also welcome.
.K

As I read it, the key problem here is not being able to remove the handler; if so, all you need it to store the delegate (where in the below, YourDelegateType is meant to mean: the defined type of GetChartDataCompleted):
YourDelegateType handler = (se, ea) =>
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr = null;
};
_csr.ServiceClient.GetChartDataCompleted += handler;
...
_csr.ServiceClient.GetChartDataCompleted -= handler;
You can also make it self-unsubscribing (i.e. so that it unsubscribes when the event is raised):
YourDelegateType handler = null;
handler = (se, ea) =>
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr.ServiceClient.GetChartDataCompleted -= handler;
_csr = null;
};
_csr.ServiceClient.GetChartDataCompleted += handler;

No, you can't do this because it's the class which raises the GetChartDataCompleted event which creates the object passed (as a reference) to the event handler. It will be creating a GetChartDataCompletedEventArgs - not an extendedEventArgs.
If you think about it, it's like trying to implement an interface which looks like this:
public interface IFoo
{
void Foo(object x);
}
with a class like this:
public class Bar : IFoo
{
// We don't care if someone calling IFoo wants to pass us something
// other than a string - we want a string, darn it!
public void Foo(string y)
{
Console.WriteLine(y.Length);
}
}
That's clearly not going to work...
Marc has shown one approach to fixing it - but I'd also point out that you should probably actually only be removing the delegate when the event fires. I'm assuming that the fact that the method is called GetChartDataAsync means it's a non-blocking method... so unsubscribing from the event immediately after calling it probably isn't a great idea.

If you'd prefer to avoid the anonymous methods, you can manually do essentially what the compiler is doing for you under the hood. That is, create a closure class to hold the Action and a reference to itself as fields and which exposes the method you want to assign to the event. Something like this:
class RetrieveDataClosure
{
private Action action;
private MyClass self;
public RetrieveDataClosure(Action action, MyClass self)
{
this.action = action;
this.self = self;
}
public void ChartDataCompleted(object se, MyEventArgs ea)
{
self._cachedCharts = ea.Result;
self.ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
self._csr = null;
}
}
Which you'd use in your code like this:
var closure = new RetrieveDataClosure(action, this);
_csr = new CustomerServiceRepository();
_csr.ServiceClient.GetChartDataCompleted += closure.ChartDataCompleted;
_csr.ServiceClient.GetChartDataAsync(
Settings.Current.Customer.CustomerName,
pointId,
chartCollectionId);
_csr.ServiceClient.GetChartDataCompleted -= closure.ChartDataCompleted;

Related

Creating a method to subscribe to an event but fire only once

I'm trying to write a method that attaches adds itself to an event, and then removes itself after it was invoked.
Those are my failed attempts:
https://imgur.com/a/PvrPUYY
public Action myEvent = () => {};
void Bar(){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () =>
{
Debug.Log("Attempt 0");
myEvent -= invokeThenUnsubscribe;
};
myEvent += invokeThenUnsubscribe;
SubscribeOnce(myEvent, () => Debug.Log("Attempt 1"));
myEvent.SubscribeOnce(() => Debug.Log("Attempt 2"));
myEvent.Invoke();
myEvent.Invoke();
}
public Action SubscribeOnce( Action a, Action actionToSubscribe ) {
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () =>
{
actionToSubscribe.Invoke();
a -= invokeThenUnsubscribe;
};
a += invokeThenUnsubscribe;
return a;
}
public static class ActionsEx {
public static void SubscribeOnce(this Action a, Action actionToSubscribe){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () => {
actionToSubscribe.Invoke();
a -= invokeThenUnsubscribe;
};
a += invokeThenUnsubscribe;
}
}
I realize it's happening because, after using +=, I lose the reference to the new event.
Does anyone have any idea how I can achieve the effect I'm looking for?
Thanks in advance
When you call SubscribeOnce, you aren't passing a reference to "the event" (technically a field, but: semantics); instead, you're reading the current value of the event (field), and passing that to the method. At that point, it the parameter value is completely divorced from the event (field), and no change to a (now a captured parameter) will have any effect on the original event (field). You're only "unsubscribing" a local delegate that has nothing to do with the event (field).
To do what you'd want, you'd need to pass the originating object in as the parameter, i.e. something like:
public static void SubscribeOnce(this SomeType obj, Action actionToSubscribe){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () => {
actionToSubscribe.Invoke();
obj.TheEvent -= invokeThenUnsubscribe;
};
obj.TheEvent += invokeThenUnsubscribe;
}
}
where SomeType (which could be an interface) defines an event Action TheEvent;, with usage this.SubscribeOnce(() => Debug.Log("Attempt 2")); (or whatever object you want to use)
I am not crazy about the fact that you are mixing actions with events. Instead, I would use a more object oriented approach and separate the two concepts by creating an interface to represent the subscribable objects that expose some event and then use an extension method to allow all actions to subscribe once to these objects:
//Delegate to handle events
public delegate void SomeEventEventHandler(ISubscribable sender, EventArgs args);
//Interface that describes an object you can subscribe to
public interface ISubscribable
{
event SomeEventEventHandler MyEvent;
}
//Implementation of a subscribable object (base class)
public class SomeObject : ISubscribable
{
private SomeEventEventHandler _handler;
public event SomeEventEventHandler MyEvent
{
add { _handler += value; }
remove { _handler -= value; }
}
public void RaiseEvent()
{
_handler?.Invoke(this, new EventArgs());
}
}
//Extension method for actions
public static class ActionExtensions
{
public static void SubscribeOnce(this Action action, ISubscribable subscribable)
{
SomeEventEventHandler handler = null;
handler = new SomeEventEventHandler((ISubscribable subscribable, EventArgs args) =>
{
action();
subscribable.MyEvent -= handler;
});
subscribable.MyEvent += handler;
}
}
//Usage:
var obj = new SomeObject();
obj.MyEvent += (ISubscribable sender, EventArgs args) =>
{
Console.WriteLine("Attempt 0 (always fires).");
};
Action invokeThenUnsubscribe = () => { Console.WriteLine("Attempt 1"); };
invokeThenUnsubscribe.SubscribeOnce(obj);
Action invokeThenUnsubscribe2 = () => { Console.WriteLine("Attempt 2"); };
invokeThenUnsubscribe2.SubscribeOnce(obj);
obj.RaiseEvent();
obj.RaiseEvent();
obj.RaiseEvent();
Output:
Attempt 0 (always fires).
Attempt 1
Attempt 2
Attempt 0 (always fires).
Attempt 0 (always fires).
Alternatively, you can skip the action extension all together and provide this functionality as a method of the class:
//In class SomeObject
public void SubscribeOnce(Action action)
{
SomeEventEventHandler handler = null;
handler = new SomeEventEventHandler((ISubscribable subscribable, EventArgs args) =>
{
action();
subscribable.MyEvent -= handler;
});
this.MyEvent += handler;
}
//Usage:
var obj2 = new SomeObject();
obj2.MyEvent += (ISubscribable sender, EventArgs args) =>
{
Console.WriteLine("Attempt 0 (always fires).");
};
obj2.SubscribeOnce(() => { Console.WriteLine("Attempt 1"); });
obj2.SubscribeOnce(() => { Console.WriteLine("Attempt 2"); });
obj2.RaiseEvent();
obj2.RaiseEvent();
obj2.RaiseEvent();
This produces the same output.

How to remove a method from Action with parameter? [duplicate]

Is it possible to unsubscribe an anonymous method from an event?
If I subscribe to an event like this:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
I can un-subscribe like this:
MyEvent -= MyMethod;
But if I subscribe using an anonymous method:
MyEvent += delegate(){Console.WriteLine("I did it!");};
is it possible to unsubscribe this anonymous method? If so, how?
Action myDelegate = delegate(){Console.WriteLine("I did it!");};
MyEvent += myDelegate;
// .... later
MyEvent -= myDelegate;
Just keep a reference to the delegate around.
One technique is to declare a variable to hold the anonymous method which would then be available inside the anonymous method itself. This worked for me because the desired behavior was to unsubscribe after the event was handled.
Example:
MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
Since C# 7.0 local functions feature has been released, the approach suggested by J c becomes really neat.
void foo(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
So, honestly, you do not have an anonymous function as a variable here. But I suppose the motivation to use it in your case can be applied to local functions.
From memory, the specification explicitly doesn't guarantee the behaviour either way when it comes to equivalence of delegates created with anonymous methods.
If you need to unsubscribe, you should either use a "normal" method or retain the delegate somewhere else so you can unsubscribe with exactly the same delegate you used to subscribe.
In 3.0 can be shortened to:
MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
Instead of keeping a reference to any delegate you can instrument your class in order to give the event's invocation list back to the caller. Basically you can write something like this (assuming that MyEvent is declared inside MyClass):
public class MyClass
{
public event EventHandler MyEvent;
public IEnumerable<EventHandler> GetMyEventHandlers()
{
return from d in MyEvent.GetInvocationList()
select (EventHandler)d;
}
}
So you can access the whole invocation list from outside MyClass and unsubscribe any handler you want. For instance:
myClass.MyEvent -= myClass.GetMyEventHandlers().Last();
I've written a full post about this tecnique here.
Kind of lame approach:
public class SomeClass
{
private readonly IList<Action> _eventList = new List<Action>();
...
public event Action OnDoSomething
{
add {
_eventList.Add(value);
}
remove {
_eventList.Remove(value);
}
}
}
Override the event add/remove methods.
Keep a list of those event handlers.
When needed, clear them all and re-add the others.
This may not work or be the most efficient method, but should get the job done.
If you want to be able to control unsubscription then you need to go the route indicated in your accepted answer. However, if you are just concerned about clearing up references when your subscribing class goes out of scope, then there is another (slightly convoluted) solution which involves using weak references. I've just posted a question and answer on this topic.
One simple solution:
just pass the eventhandle variable as parameter to itself.
Event if you have the case that you cannot access the original created variable because of multithreading, you can use this:
MyEventHandler foo = null;
foo = (s, ev, mehi) => MyMethod(s, ev, foo);
MyEvent += foo;
void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance)
{
MyEvent -= myEventHandlerInstance;
Console.WriteLine("I did it!");
}
If the best way is to keep a reference on the subscribed eventHandler, this can be achieved using a Dictionary.
In this example, I have to use a anonymous method to include the mergeColumn parameter for a set of DataGridViews.
Using the MergeColumn method with the enable parameter set to true enables the event while using it with false disables it.
static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();
public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {
if(enable) {
subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
dg.Paint += subscriptions[dg];
}
else {
if(subscriptions.ContainsKey(dg)) {
dg.Paint -= subscriptions[dg];
subscriptions.Remove(dg);
}
}
}
if you want refer to some object with this delegate, may be you can use Delegate.CreateDelegate(Type, Object target, MethodInfo methodInfo)
.net consider the delegate equals by target and methodInfo
There is a way to solve this by implementing the closure yourself instead of a lambda expression.
Assume that the class to be used as a capture variable is as follows.
public class A
{
public void DoSomething()
{
...
}
}
public class B
{
public void DoSomething()
{
...
}
}
public class C
{
public void DoSomething()
{
...
}
}
These classes will be used as capture variables, so we instantiate them.
A a = new A();
B b = new B();
C c = new C();
Implement the closure class as shown below.
private class EventHandlerClosure
{
public A a;
public B b;
public C c;
public event EventHandler Finished;
public void MyMethod(object, MyEventArgs args)
{
a.DoSomething();
b.DoSomething();
c.DoSomething();
Console.WriteLine("I did it!");
Finished?.Invoke(this, EventArgs.Empty);
}
}
Instantiate the closure class, create a handler, then subscribe to the event and subscribe to the lambda expression that unsubscribes from the closure class's Finished event.
var closure = new EventHandlerClosure
{
a = a,
b = b,
c = c
};
var handler = new MyEventHandler(closure.MyMethod);
MyEvent += handler;
closure.Finished += (s, e)
{
MyEvent -= handler;
}
I discovered this quite old thread recently for a C# project and found all the answers very useful. However, there was one aspect that didn't work well for my particular use case - they all put the burden of unsubscribing from an event on the subscriber. I understand that one could make the argument that it's the subscribers job to handle this, however that isn't realistic for my project.
My primary use case for events is for listening to timers to sequence animations (it's a game). In this scenario, I use a lot of anonymous delegates to chain together sequences. Storing a reference to these isn't very practical.
In order to solve this, I've created a wrapper class around an event that lets you subscribe for a single invocation.
internal class EventWrapper<TEventArgs> {
private event EventHandler<TEventArgs> Event;
private readonly HashSet<EventHandler<TEventArgs>> _subscribeOnces;
internal EventWrapper() {
_subscribeOnces = new HashSet<EventHandler<TEventArgs>>();
}
internal void Subscribe(EventHandler<TEventArgs> eventHandler) {
Event += eventHandler;
}
internal void SubscribeOnce(EventHandler<TEventArgs> eventHandler) {
_subscribeOnces.Add(eventHandler);
Event += eventHandler;
}
internal void Unsubscribe(EventHandler<TEventArgs> eventHandler) {
Event -= eventHandler;
}
internal void UnsubscribeAll() {
foreach (EventHandler<TEventArgs> eventHandler in Event?.GetInvocationList()) {
Event -= eventHandler;
}
}
internal void Invoke(Object sender, TEventArgs e) {
Event?.Invoke(sender, e);
if(_subscribeOnces.Count > 0) {
foreach (EventHandler<TEventArgs> eventHandler in _subscribeOnces) {
Event -= eventHandler;
}
_subscribeOnces.Clear();
}
}
internal void Remove() {
UnsubscribeAll();
_subscribeOnces.Clear();
}
}
The side benefit of having this in a class is that you can make it private and expose only the functionality you want. For example, only expose the SubscribeOnce (and not the Subscribe) method.
public class MyClass {
private EventWrapper<MyEventEventArgs> myEvent = new EventWrapper<MyEventEventArgs>();
public void FireMyEvent() {
myEvent.Invoke(this, new MyEventEventArgs(1000, DateTime.Now));
}
public void SubscribeOnce(EventHandler<MyEventEventArgs> eventHandler) {
myEvent.SubscribeOnce(eventHandler);
}
public class MyEventEventArgs : EventArgs {
public int MyInt;
public DateTime MyDateTime;
public MyEventEventArgs(int myInt, DateTime myDateTime) {
MyInt = myInt;
MyDateTime = myDateTime;
}
}
}
The tradeoff here is more overhead for having an instance of this for each event, however in my scenario - this is an acceptable tradeoff to ensure that garbage gets collected efficiently and the code is more maintainable on the subscriber side. Full example here.
Here is a simple solution, which removes all assigned methods from an event. Also anonymous methods.
Use this code and adjust the names.
if (MyEvent != null)
foreach (Delegate del in MyEvent.GetInvocationList())
MyEvent -= (EventHandler<MyEventHandlerType>)del;
Example usage
public class SomeClass
{
public event EventHandler<NiceEventArgs> NiceEvent;
public void RemoveHandlers()
{
if (NiceEvent != null)
foreach (Delegate del in NiceEvent.GetInvocationList())
NiceEvent -= (EventHandler<NiceEventArgs>)del;
}
}
Thanks to hemme's answer, which I used as inspiration.

How to get compile time type safety when defining a self-removing event Action<...> handler without explicitly typing the full type of the handler?

I have a simple event handler in a class like so:
public class Eventable
{
public event Action<Eventable, TypeB> OnMagic;
// ...
}
I would like to be able to declare a type safe handler by reusing the type of OnMagic, in order to have a reference which can be removed from that same event when a condition is met, like so:
static class Program
{
static T HandlerOf<T>(T _)
{
return default(T);
}
static void Main(string[] args)
{
var eventable = new Eventable();
var myHandler = HandlerOf(eventable.OnMagic);
myHandler = (that, myB) => { eventable.OnMagic -= myHandler };
eventable.OnMagic += myHandler;
}
// ...
}
This would stop me from retyping sometimes very long class names in my code and would improve readability, however VS2015 objects with this error:
The event 'eventable.OnMagic' can only appear on the left hand side of += or -= (except when used from within the type 'Eventable')
Is there an other way to achieve this, to hide completly the type of the event handler, without throwing out type safety?
Thank you.
What's wrong with:
Action<Eventable, TypeB> myHander = null;
myHandler = (that, myB) => { eventable.OnMagic -= myHandler };
eventable.OnMagic += myHandler;
?
After all, you already know the type of myHander, so you don't need to go through the contortion of HandlerOf<T>(T _) (which might be useful for dealing with anonymous types... I don't see any in your code)
If what you want is to register an action that when called unregister itself you could do something like:
public class Eventable
{
public event Action<Eventable, int> OnMagic;
public void Magic(int i)
{
OnMagic?.Invoke(this, i);
}
public void RegisterMagic(Action<Eventable, int> action)
{
Action<Eventable, int> _action = null;
_action = (_e, i) =>
{
action(_e, i);
OnMagic -= _action;
};
OnMagic += _action;
}
}
class Program
{
static void Main(string[] args)
{
Eventable e = new Eventable();
e.RegisterMagic((ev, i) => Console.WriteLine(i));
e.RegisterMagic((ev, i) => Console.WriteLine(i * i));
e.Magic(4);
e.Magic(5);
}
}
Output:
4
16
Note: I put the RegisterMagic in the Eventable class just in case this behavior is for Evantable objects only. But this can be modified as your needs demmand.

NullReferenceException when attempting to fire an event

I'm learning about creating events and creating multi-threaded applications.
The method Thread is called by another class which populates the params with search conditions. A BackgroundWorker is created, performs a search and returns the results to worker_RunWorkerCompleted.
Within worker_RunWorkerCompleted, I want to send the results back to my UI which is subscribing to the Fireendofsearch event.
I'm having trouble understanding why my code below throws the following error
Object reference not set to an instance of an object.
when I fire the event Fireendofsearch
public class BackgroundSearch
{
public event SearchResultCompleteThreaded Fireendofsearch;
public EventArgs a = null;
public delegate void SearchResultCompleteThreaded(object seachresults, EventArgs a);
internal void Thread(string folder, string parms)
{
var Argument = new List<object> { folder, parms };
var worker = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = false};
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync(Argument);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var passedAugue = e.Argument as List<object>;
var returnResult = new List<string[]>();
if (passedAugue != null)
{
var result = Directory.GetFiles(passedAugue[0].ToString(), passedAugue[1].ToString(), SearchOption.AllDirectories);
foreach (string s in result)
{
var t = new string[4];
t[0] = s;
t[1] = File.GetCreationTime(s).ToString();
t[2] = File.GetLastAccessTime(s).ToString();
t[3] = File.GetLastWriteTime(s).ToString();
returnResult.Add(t);
}
}
if (returnResult.Count != 0)
{
e.Result = returnResult;
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
Fireendofsearch(e.Result, a);
}
}
}
Firendofsearch will be null until someone subscribes to it, change your work completed event handler to this to fix it.
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
var friendOfSearch = Fireendofsearch;
if(friendOfSearch != null)
friendOfSearch (e.Result, a);
}
}
The reason I copy it to a variable is if someone in another thread is the last person to unsubscribe between the null check and the raising of the event you will still get the null reference exception, by coping to another variable first it solves that problem.
However I would make some other changes if I where writing it, you are retuning a null EventArgs for some reason and passing the result back as the "Sender" in the traditional event pattern. I would change your code to this
public event EventHandler<FriendOfSearchArgs> FirendsOfSearch;
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
RaiseFriendOfSearch(e.Result);
}
}
protected virtual void RaiseFriendOfSearch(object result)
{
var friendOfSearch = FirendsOfSearch;
if(friendOfSearch != null)
friendOfSearch(this, new FriendOfSearchArgs(result));
}
public class FriendOfSearchArgs : EventArgs
{
public FriendOfSearchArgs(object result)
{
Result = result;
}
public object Result {get; private set;}
}
This was all written in the SO text box so there may be one or two errors.
You should check for null before trying to invoke the delegate. And you have to pull it into a separate variable first to avoid threading issues.
var ev = Fireendofsearch;
if ( ev != null ) ev( ... );
I have also found it useful to have an extension method for this case:
public static void Raise ( this EventHandler h, object sender )
{
if ( h != null) h( sender, EventArgs.Empty );
}
And then:
MyEvent.Raise ( this );
"Behind" your public event, there's an implicit private variable of type SearchResultCompleteThreaded. The type SearchResultCompleteThreaded is a delegate type.
In .NET all delegates are "multicast" delegates. That means they have an invocation list (your GetInvocationList() method on SearchResultCompleteThreaded is derived from System.Delegate.GetInvocationList().
Now, in .NET, the invocation list is guaranteed to consist of one or more items (not zero or more). Any delegate type is an immutable type. But if you try to create a new instance by "subtracting away" all members in the invocation list of an existing instance, as in var newDel = oldDel - oldDel; or reuseDel -= reuseDel;, then instead of getting a new instance with a zero-length invocation list, you get a null reference!
The good thing about this is that you don't have to worry about a subtle difference between an "empty" delegate instance (which could otherwise have been permitted) and a null reference. The bad thing about it, is the problem you had above.
For some reason (Optimization most likely), an event is only instantiated when a first handler method subscribe to it.
You must check for that in your code.
Here's how I usually declare an event:
public event SearchResultCompleteThreaded Fireendofsearch;
private void RaiseFireEndOfSearchEvent(EventArgs e)
{
if (Fireendofsearch != null)
{
Fireendofsearch(this, e);
}
}
And whenever I need to raise the event, I would just call the helper method instead.
Set:
public event SearchResultCompleteThreaded Fireendofsearch = delegate { };
Needs initialized possibly?

How to pass an event to a method?

I would like to create a method that takes an event as an argument and adds eventHandler to it to handle it properly. Like this:
I have two events:
public event EventHandler Click;
public event EventHandler Click2;
Now I would like to pass a particular event to my method like this (pseudocode):
public AttachToHandleEvent(EventHandler MyEvent)
{
MyEvent += Item_Click;
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);
Is it possible?
I've noticed that this code worked fine, and returned to my project and noticed that when I pass an event declared in my class, it works, but when I pass event from other class it still does not work.
What I get is this error:
The event
'System.Windows.Forms.ToolStripItem.Click'
can only appear on the left hand side
of += or -=
My original answer was suitable from within the class that defined the event, but you've since updated your question to reflect that you wish to accomplish this from outside the defining class, so I've stricken that.
Only the class that defines an event can refer to the implicit delegate variable that the event uses. From outside that class, you only have access to the add and remove methods, via += and -=. This means that you can't do what you're asking, directly. You can, however, use a functional approach.
class A{
public event EventHandler Event1;
public void TriggerEvent1(){
if(Event1 != null)
Event1(this, EventArgs.Empty);
}
}
class B{
static void HandleEvent(object o, EventArgs e){
Console.WriteLine("Woo-hoo!");
}
static void AttachToEvent(Action<EventHandler> attach){
attach(HandleEvent);
}
static void Main(){
A a = new A();
AttachToEvent(handler=>a.Event1 += handler);
a.TriggerEvent1();
}
}
I did it like this:
public AttachToHandleEvent(Object obj, string EventName)
{
EventInfo mfi = obj.GetType().GetEvent(EventName);
MethodInfo mobj = mfi.GetAddMethod();
mobj.Invoke(obj, new object[] { Item_Click});
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool "Click");
Thank you all for advice. This solution could not be done without your help.
It's not possible. You can use a delegate instead of an event if that meets your needs.
Just write tool.Click += Item_Click;
Edit: From MSDN "Events can only be invoked from within the class or struct where they (it) are declared". So what you are trying to do is not possible. Could you elaborate more on your needs? Why would you want to pass an event as a parameter?
delegate void doIt(object sender, object data);
event doIt OnDoIt;
void add(doIt theDel)
{
OnDoIt += theDel;
}
void doIt1(object a, object b)
{
}
void doIt2(object a, object b)
{
}
void add()
{
add(doIt1);
add(doIt2);
}
Your question suggests that you got some mechanisms wrong:
You can't pass events!
You most probably want to pass a function as a parameter, so the calling method will call that other method at some point. In technical terms this is a delegate. I suggest using the already defined Action class. Here's an example snippet:
void MyFunction (string otherArguments, Action onFinished){
...
if (onFinished != null)
onFinished.Invoke();
}
The nice thing about this is that when calling MyFunction you can declare the Action using the inline syntax:
MyFunction("my other argument", ()=>{
///do stuff here, which will be execuded when the action is invoked
});
I pass functions/methods (instead of events) like this:
class A
{
public void something()
{
var myAction =
new Action<object, object>((sender, evArgs) => {
MessageBox.Show("hiii, event happens " + (evArgs as as System.Timers.ElapsedEventArgs).SignalTime);
});
B.timer(myAction);
}
}
class B
{
public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
myTimer.Interval = 2000;
myTimer.Start();
}
}
Giving an update to this question with an object oriented solution.
Instead of using an Action<EventHandler> that registers the event, you could create an object handling that for you
public class AEvent
{
private readonly A aInstance;
private AEvent(A instance) {
aInstance = instance;
}
public void Add(EventHandler eventHandler)
=> a.Event1 += eventHandler;
public void Remove(EventHandler eventHandler)
=> a.Event1 -= eventHandler;
public EventHandler Invoke => aInstance.Event1;
}
Then later on use that object like this:
static void Main(){
A a = new A();
AEvent aEvent = new AEvent(A)
aEvent.Add(handler);
a.Invoke();
}
One approach I haven't seen here would be to create an object which has delegates for subscribe and unsubscribe. Here is a complete example program.
class Program
{
private event EventHandler<EventArgs> eventHandler;
public static void Main(string[] args)
{
Program program = new Program();
Thing thing = new Thing(new EventWrapper<EventArgs>(
delegate(EventHandler<EventArgs> handler) { program.eventHandler += handler; },
delegate(EventHandler<EventArgs> handler) { program.eventHandler -= handler; }
));
// events are fired
program.eventHandler?.Invoke(program, EventArgs.Empty);
thing.Unsubscribe();
}
}
class Thing
{
private readonly Action<EventHandler<EventArgs>> _unsubscribeEventHandler;
public Thing(EventWrapper<EventArgs> eventHandler)
{
this._unsubscribeEventHandler = eventHandler.Unsubscribe;
eventHandler.Subscribe?.Invoke(OnEvent);
Console.WriteLine("subscribed");
}
private void OnEvent(object? sender, EventArgs e)
{
Console.WriteLine("event fired");
}
public void Unsubscribe()
{
_unsubscribeEventHandler?.Invoke(OnEvent);
Console.WriteLine("unsubscribed");
}
}
class EventWrapper<T> where T : EventArgs
{
public Action<EventHandler<T>> Subscribe { get; private set; }
public Action<EventHandler<T>> Unsubscribe { get; private set; }
public EventWrapper(Action<EventHandler<T>> subscribe, Action<EventHandler<T>> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
}
In this example, we created a new class called EventWrapper<T> which wraps delegates for += and -= and exposes them with Subscribe and Unsubscribe methods. The delegates will need to be created by the class which created the event.

Categories

Resources