How can I bind a handler to a static event without invoking the static constructor?
How can I bind a handler to a static event without invoking the static constructor?
I fear I know the answer to this but...
Is it possible to successfully bind to a static event that will raise during a static constructor? Or is this logically impossible?
My event handler is not being hit. I suspect it's because when I do this...
MyClass.MyEvent += MyEventHandler;
...the call to MyClass
is running the static contructor, so the event inside that constructor has already been raised by the time the handler is bound, later in that line of code.
MyClass
Is that correct? Is there another way to do this? Is it at all possible to bind to a static event without causing the static constructor to execute?
If your event is in the constructor then you need to have another handler object. You listen to that event and during the constructor of the first object have it alert the one you're listening to and that one raises the event.
– Michael Puckett II
2 days ago
3 Answers
3
The first time I read this question I immediately thought "No way...". But then I was like "hold on a second.. it may be possible.. hmmm". Ok, let's go to the code! Consider the following:
abstract class FooBaseNotifier
{
public static event Action FooTypeLoaded;
protected static void Notify() => FooTypeLoaded?.Invoke();
}
class Foo:FooBaseNotifier
{
static Foo() => Notify();
}
The key here is to play with C# static constructors rules. We use an abstract base class to define the event and a protected method to invoke it. It must be protected so that Foo can fire the event from the static constructor.
Now testing:
FooBaseNotifier.FooTypeLoaded += () => Console.WriteLine("Foo");
var foo = new Foo();
Console.ReadLine();
It works! This will print "Foo" to the console. Furthermore, due to the fact that the static event is a static member of the abstract base class you can do even this:
Foo.FooTypeLoaded += () => Console.WriteLine("Foo");
var foo = new Foo();
Console.ReadLine();
And Foo's static constructor is not called but until the line with new Foo()
is reached!
new Foo()
Short answer: no.
Longer answer: Put the handler on some other class. FooHandlers.MyEvent. Then just have the Foo static constructor trigger those events. You’ll need to create some FooHandlers.InvokeMyEvent methods, since Foo won’t be able to do it directly.
As to the “why” of this, static constructors are run before and field of the type is accessed. An event has a backing field to hold the multicast delegate. That means accessing the event triggers the static constructor unavoidably before it can be assigned (that’s sort of the point of a static constructor, after all).
I generally try to avoid static constructors, because they tend to cause weird problems like this one... and if they ever throw any sort of exception, the behavior is strange.
When you declare an event using the event
keyword, you are really declaring two methods, add
and remove
. Think of it like when you declare a property: under the covers, you are really declaring a set
and a get
method. Events are no different; you can actually override add
and remove
in code, with a custom event accesssor.
event
add
remove
set
get
add
remove
So when you call
MyClass.MyEvent += MyEventHandler;
you are really calling
MyClass.MyEvent.add(MyEventHandler); //not real code
Naturally, the static constructor has to be run whenever any method is accessed, to ensure the static state of the class is correct. It's a feature. So to defer execution of the static constructor while adding to the event is not possible, I'm afraid.
If you want some initialization to run later, extract it to a separate method and call it separately, or load it lazily, as in my example. Another approach would be to assign the handler lazily, as taquion suggests in his answer. I only prefer my answer because it's a common pattern, and I'd like other engineers to understand what is going on in my code, especially if it is critical for your application that the initialization logic execute at a specific time.
static public class MyClass
{
static bool _initialized = false;
static MyClass()
{
Console.WriteLine("Test.ctor called");
}
static void Initialize()
{
Console.WriteLine("Test.Initialize called");
_initialized = true;
}
static public event EventHandler MyEvent;
static public void RaiseMyEvent()
{
if (!_initialized) Initialize();
if (MyEvent != null) MyEvent(typeof(MyClass), new EventArgs());
}
}
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
..the event inside that constructor..? could you show your class definition or better, convert this into a minimal, complete example we could run?
– dlatikay
2 days ago