Single-shot event subscription - c#

I'm fairly convinced that this isn't possible, but I'm going to ask nonetheless.
In order to make a single-shot subscription to events, I frequently find myself using this (self-invented) pattern:
EventHandler handler=null;
handler = (sender, e) =>
{
SomeEvent -= handler;
Initialize();
};
SomeEvent += handler;
It's quite a lot of boiler-plate, and it also makes Resharper whinge about modified closures. Is there a way of turning this pattern into an extension method or similar? A better way of doing it?
Ideally, I'd like something like:
SomeEvent.OneShot(handler)

It's not very easy to refactor to an extension method, because the only way you can refer to an event in C# is by subscribing (+=) to or unsubscribing (-=) from it (unless it's declared in the current class).
You could use the same approach as in Reactive Extensions: Observable.FromEvent takes two delegates to subscribe to the event an unsubscribe from it. So you could do something like that:
public static class EventHelper
{
public static void SubscribeOneShot(
Action<EventHandler> subscribe,
Action<EventHandler> unsubscribe,
EventHandler handler)
{
EventHandler actualHandler = null;
actualHandler = (sender, e) =>
{
unsubscribe(actualHandler);
handler(sender, e);
};
subscribe(actualHandler);
}
}
...
Foo f = new Foo();
EventHelper.SubscribeOneShot(
handler => f.Bar += handler,
handler => f.Bar -= handler,
(sender, e) => { /* whatever */ });

The following code works for me. It's not perfect to have to specify the event via a string, but I have no glue how to solve that. I guess it's not possible in the current C# version.
using System;
using System.Reflection;
namespace TestProject
{
public delegate void MyEventHandler(object sender, EventArgs e);
public class MyClass
{
public event MyEventHandler MyEvent;
public void TriggerMyEvent()
{
if (MyEvent != null)
{
MyEvent(null, null);
}
else
{
Console.WriteLine("No event handler registered.");
}
}
}
public static class MyExt
{
public static void OneShot<TA>(this TA instance, string eventName, MyEventHandler handler)
{
EventInfo i = typeof (TA).GetEvent(eventName);
MyEventHandler newHandler = null;
newHandler = (sender, e) =>
{
handler(sender, e);
i.RemoveEventHandler(instance, newHandler);
};
i.AddEventHandler(instance, newHandler);
}
}
public class Program
{
static void Main(string[] args)
{
MyClass c = new MyClass();
c.OneShot("MyEvent",(sender,e) => Console.WriteLine("Handler executed."));
c.TriggerMyEvent();
c.TriggerMyEvent();
}
}
}

I would suggest using a "custom" event so that you have access to the invocation list, and then raise the event by using Interlocked.Exchange to simultaneously read and clear the invocation list. If desired, event subscription/unsubscription/raising could be done in thread-safe manner by using a simple linked-list stack; when the event is raised, the code could, after the Interlocked.Exchange, reverse the order of stack items. For the unsubscribe method, I'd probably suggest simply setting a flag within the invocation-list item. This could in theory cause a memory leak if events were repeatedly subscribed and unsubscribed without the event ever being raised, but it would make for a very easy thread-safe unsubscribe method. If one wanted to avoid a memory leak, one could keep a count of how many unsubscribed events are still in the list; if too many unsubscribed events are in the list when an attempt is made to add a new one, the add method could go through the list and remove them. Still workable in entirely lock-free thread-safe code, but more complicated.

Related

How to remove yourself from an event handler?

What I want to do is basically remove a function from an event, without knowing the function's name.
I have a FileSystemWatcher. If a file is created/renamed it checks its name. If it matches, it then moves it to a specific location. However, if the file is locked, it makes a lambda that attaches to a timer's tick event, waiting until the file is not locked. When it isn't, it moves the file and then removes itself from the event handler. I've seen lots of ways to do this, like keeping the instance, or making a named method. I can't do either of those here. What are my options?
There is no simple method to achieve this.
Preferred approach:
I don't see why you can't save the delegate. You don't have to save the instance as some field. It can be a local variable that is captured by your anonymous event handler:
EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
// Do whatever you need to do here
// Remove event:
foo.Event -= handler;
}
foo.Event += handler;
I can't think of a single scenario where you can't use this.
Alternative approach without saving the delegate:
However, if you have such a scenario, it get's quite tricky.
You need to find the delegate that has been added as a handler to the event. Because you didn't save it, it is pretty hard to obtain it. There is no this to get a delegate of the currently executing method.
You can't use GetInvocationList() on the event either, because accessing an event outside the class it is defined in is restricted to adding and removing handlers, i.e. += and -=.
Creating a new delegate isn't possible either. While you can get access to the MethodInfo object defining your anonymous method, you can't get access to the instance of the class that method is declared in. This class is generated automatically by the compiler and calling this inside the anonymous method will return the instance of the class your normal method is defined in.
The only way I found that works is to find the field - if any - that the event uses and call GetInvocationList() on it. The following code demonstrates this with a dummy class:
void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => {
Console.WriteLine("Executed");
var self = new StackFrame().GetMethod();
var eventField = foo.GetType()
.GetField("Bar", BindingFlags.NonPublic |
BindingFlags.Instance);
if(eventField == null)
return;
var eventValue = eventField.GetValue(foo) as EventHandler;
if(eventValue == null)
return;
var eventHandler = eventValue.GetInvocationList()
.OfType<EventHandler>()
.FirstOrDefault(x => x.Method == self)
as EventHandler;
if(eventHandler != null)
foo.Bar -= eventHandler;
};
foo.RaiseBar();
foo.RaiseBar();
}
public class Foo
{
public event EventHandler Bar;
public void RaiseBar()
{
var handler = Bar;
if(handler != null)
handler(this, EventArgs.Empty);
}
}
Please note that the string "Bar" that is passed to GetField needs to be the exact name of the field that is used by the event. This results in two problems:
The field can be named differently, e.g. when using an explicit event implementation. You need to manually find out the field name.
There might be no field at all. This happens if the event uses an explicit event implementation and just delegates to another event or stores the delegates in some other way.
Conclusion:
The alternative approach relies on implementation details, so don't use it if you can avoid it.
Steps to remove event handler with lambda expression:
public partial class Form1 : Form
{
private dynamic myEventHandler;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there"));
this.button1.Click += myEventHandler;
}
private void button1_Click(object sender, EventArgs e, string additionalInfo)
{
MessageBox.Show(additionalInfo);
button1.Click -= myEventHandler;
}
}

Checking existing 'wired up' methods

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!

Event handler/raiser code snippet

I would like to hear opinions on below code snippet. Is there anything that can be improved? Is the event handler/raiser naming following best practices? I know it is not that useful to handle and raise events in the same class but this is just a snippet.
public class MyControl
{
public MyControl()
{
this.LogWritten += this.HandleMyControlLogWritten;
}
// Event handler
void HandleMyControlLogWritten(object sender, EventArgs e)
{
}
// Event object
public event Action<object, EventArgs> LogWritten;
// Event raiser
protected virtual void OnLogWritten(EventArgs e)
{
if (this.LogWritten != null)
{
this.LogWritten(this, e);
}
}
}
The main change I'd recommend would be to get a copy of the event handler:
// Event raiser
protected virtual void OnLogWritten(EventArgs e)
{
var handler = this.LogWritten;
if (handler != null)
{
handler(this, e);
}
}
This is important if you're planning to (eventually) use this class in a multi-threaded scenario. As such, I find that it's a good "best practice" to get into the habit of using. The issue is that, when using in multiple threads, without creating the copy, it's possible that the only "handler" attached could unsubscribe between the null check and the invocation, which would cause a runtime error. By copying to a temporary variable (the var handler = this.LogWritten;) line, you're effectively creating a "snapshot" of the subscriber list, and then checking it for null and invoking if required.
The other change is in the event declaration itself. Instead of using Action<T1,T2>:
// Event object
public event Action<object, EventArgs> LogWritten;
I would recommend using EventHandler<TEventArgs> (if you want to use a custom EventArgs subclass) or EventHandler (for standard EventArgs). These are more "standard practice", and will be what other developers expect:
// Event object
public event EventHandler LogWritten;

C# How to find if an event is hooked up

I want to be able to find out if an event is hooked up or not. I've looked around, but I've only found solutions that involved modifying the internals of the object that contains the event. I don't want to do this.
Here is some test code that I thought would work:
// Create a new event handler that takes in the function I want to execute when the event fires
EventHandler myEventHandler = new EventHandler(myObject_SomeEvent);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length;
// Now actually hook an event up
myObject.SomeEvent += m_myEventHandler;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length;
Unfort the above is dead wrong. I thought that somehow the "invocationList" in myEventHandler would automatically get updated when I hooked an event to it. But no, this is not the case. The length of this always comes back as one.
Is there anyway to determine this from outside the object that contains the event?
If the object concerned has specified the event keyword, then the only things you can do are add (+=) and remove (-=) handlers, nothing more.
I believe that comparing the invocation list length would work, but you need to be operating inside the object to get at it.
Also, keep in mind that the += and -= operators return a new event object; they don't modify an existing one.
Why do you want to know if a particular event is hooked up? Is it to avoid registering multiple times?
If so, the trick is to remove the handler first (-=) as removing a handler that's not there is legal, and does nothing. Eg:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
There is a subtle illusion presented by the C# event keyword and that is that an event has an invocation list.
If you declare the event using the C# event keyword, the compiler will generate a private delegate in your class, and manage it for you. Whenever you subscribe to the event, the compiler-generated add method is invoked, which appends the event handler to the delegate's invocation list. There is no explicit invocation list for the event.
Thus, the only way to get at the delegate's invocation list is to preferably:
Use reflection to access the compiler-generated delegate OR
Create a non-private delegate (perhaps internal) and implement the event's add/remove methods manually (this prevents the compiler from generating the event's default implementation)
Here is an example demonstrating the latter technique.
class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}
It can be done, but it takes some hackery... as mentioned above the compiler generates the implementation of the event, including its backing field. Reflection lets you retrieve the backing field by name, and once you have access to it you can call GetInvocationList() even though you're outside the class itself.
Since you're asking to use reflection to get the event by name I assume you're also using reflection to get the Type by name--I'm whipping up an example that will show how to do it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string typeName = "ConsoleApplication1.SomeClass, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
string eventName = "SomeEvent";
Type declaringType = Type.GetType(typeName);
object target = Activator.CreateInstance(declaringType);
EventHandler eventDelegate;
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null) { Console.WriteLine("No listeners"); }
// attach a listener
SomeClass bleh = (SomeClass)target;
bleh.SomeEvent += delegate { };
//
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null)
{
Console.WriteLine("No listeners");
}
else
{
Console.WriteLine("Listeners: " + eventDelegate.GetInvocationList().Length);
}
Console.ReadKey();
}
static EventHandler GetEventHandler(object classInstance, string eventName)
{
Type classType = classInstance.GetType();
FieldInfo eventField = classType.GetField(eventName, BindingFlags.GetField
| BindingFlags.NonPublic
| BindingFlags.Instance);
EventHandler eventDelegate = (EventHandler)eventField.GetValue(classInstance);
// eventDelegate will be null if no listeners are attached to the event
if (eventDelegate == null)
{
return null;
}
return eventDelegate;
}
}
class SomeClass
{
public event EventHandler SomeEvent;
}
}
You should be able to get the invocation list via the "event". Roughly, it will be something like..
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
}
I used your example and modified it a little bit. registering an event handler increases the number of invocations. even when using two different callback methods (as shown here) or using the same callback method.
private void SomeMethod()
{
// Create a new event handler that takes in the function I want to execute when the event fires
var myEventHandler = new EventHandler(OnPropertyChanged);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length; // 1
// Now actually hook an event up
myEventHandler += OnPropertyChanged2;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length; // 2
myEventHandler.Invoke(null, null);
// each of the registered callback methods are executed once.
// or if the same callback is used, then twice.
}
private void OnPropertyChanged2(object? sender, EventArgs e)
{}
private void OnPropertyChanged(object? sender, EventArgs e)
{}
As others already mentioned, the access to eventhandler.GetInvocationList is limited to the class itself, you need to expose a property or method to retrieve the delegate list.
Like this:
protected Delegate[]? GetInvocations() => PropertyChanged?.GetInvocationList();
depending on your usage make it protected, internal or both.

How to raise custom event from a Static Class

I have a static class that I would like to raise an event as part of a try catch block within a static method of that class.
For example in this method I would like to raise a custom event in the catch.
public static void saveMyMessage(String message)
{
try
{
//Do Database stuff
}
catch (Exception e)
{
//Raise custom event here
}
}
Thank you.
Important: be very careful about subscribing to a static event from instances. Static-to-static is fine, but a subscription from a static event to an instance handler is a great (read: very dangerous) way to keep that instance alive forever. GC will see the link, and will not collect the instance unless you unsubscribe (or use something like a WeakReference).
The pattern for creating static events is the same as instance events, just with static:
public static event EventHandler SomeEvent;
To make life easier (re null checking), a useful trick here is to add a trivial handler:
public static event EventHandler SomeEvent = delegate {};
Then you can simply invoke it without the null-check:
SomeEvent(null, EventArgs.Empty);
Note that because delegate instances are immutable, and de-referencing is thread-safe, there is never a race condition here, and no need to lock... who-ever is subscribed when we de-reference gets invoked.
(adjust for your own event-args etc).
This trick applies equally to instance events.
Your event would also need to be static:
public class ErrorEventArgs : EventArgs
{
private Exception error;
private string message;
public ErrorEventArgs(Exception ex, string msg)
{
error = ex;
message = msg;
}
public Exception Error
{
get { return error; }
}
public string Message
{
get { return message; }
}
}
public static class Service
{
public static EventHandler<ErrorEventArgs> OnError;
public static void SaveMyMessage(String message)
{
EventHandler<ErrorEventArgs> errorEvent = OnError;
if (errorEvent != null)
{
errorEvent(null, new ErrorEventArgs(null, message));
}
}
}
And Usage:
public class Test
{
public void OnError(object sender, ErrorEventArgs args)
{
Console.WriteLine(args.Message);
}
}
Test t = new Test();
Service.OnError += t.OnError;
Service.SaveMyMessage("Test message");
Several folks have offered up code examples, just don't fire an event using code such as:
if(null != ExampleEvent)
{
ExampleEvent(/* put parameters here, for events: sender, eventArgs */);
}
as this contains a race condition between when you check the event for null and when you actually fire the event. Instead use a simple variation:
MyEvent exampleEventCopy = ExampleEvent;
if(null != exampleEventCopy)
{
exampleEventCopy(/* put parameters here, for events: sender, eventArgs */);
}
This will copy any event subscribers into the exampleEventCopy, which you can then use as a local-only version of the public event without having to worry about any race conditions (Essentially, it is possible that another thread could pre-empt you right after you have checked the public event for null and proceed to remove all subscribers from the event, causing the subsequent firing of the event to throw an exception, by using a local-only copy, you avoid the possibility of another thread removing subscribers, since there is no way they could access the local variable).
Note: VS2008, C#
Just declare an event as you normally would within the static class, but be sure to mark the event as static:
public static event EventHandler Work;
Then just subscribe to it as you normally would.
Just to add "Delegates are immutable" So, as shown in the example above the following line obtains a copy of the delegate.
EventHandler<ErrorEventArgs> errorEvent = OnError;
The way I did this is the following:
1- define a delegate (this will enable you to have customized arguments):
public delegate void CustomeEventHandler(string str);
2- define an event based on the previously defined delegate:
public static event CustomeEventHandler ReadLine;
3- create an event handler:
static void OnLineRead(string currentLine)
{
if (ReadLine != null)
ReadLine(currentLine);
}
4- raise your event using the event handler (just call it wherever you want the event to be raised).

Categories