如何在不调用静态构造函数的情况下将处理程序绑定到静态事件?

How can I bind a handler to a static event without invoking the static constructor?

恐怕我知道这个问题的答案但是...

是否可以成功绑定到将在静态构造函数期间引发的静态事件?或者这在逻辑上是不可能的?

我的事件处理程序没有被命中。我怀疑是因为当我这样做时...

MyClass.MyEvent += MyEventHandler;

...对 MyClass 的调用是 运行 静态构造函数,因此在绑定处理程序时,该构造函数内的事件已经引发,稍后在该行代码中.

对吗?还有另一种方法吗?是否可以在不导致静态构造函数执行的情况下绑定到静态事件?

当您使用 event 关键字声明一个事件时,您实际上是在声明两个方法,addremove。把它想象成当你声明一个 属性 时:在幕后,你实际上是在声明一个 set 和一个 get 方法。事件也不例外。您实际上可以在代码 .

中覆盖 addremove

所以当你打电话时

MyClass.MyEvent += MyEventHandler;

你真的在打电话

MyClass.MyEvent.add(MyEventHandler);  //not real code

自然是静态构造函数has to be run whenever any method is accessed,保证class的静态状态是正确的。这是一个特点。因此,恐怕无法在添加到事件的同时延迟静态构造函数的执行。

如果稍后要对 运行 进行一些初始化,请将其提取到一个单独的方法中并单独调用它,或者像我的示例那样延迟加载。另一种方法是懒惰地分配处理程序,正如 taquion 在他的回答中所建议的那样。我更喜欢我的答案,因为它是一种常见的模式,我希望其他工程师了解我的代码中发生了什么,尤其是当初始化逻辑在特定时间执行对您的应用程序至关重要时。

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());
    }
}

第一次读到这个问题时,我立刻想到了"No way..."。但后来我就像 "hold on a second.. it may be possible.. hmmm"。好的,让我们进入代码!考虑以下因素:

abstract class FooBaseNotifier
{
    public static event Action FooTypeLoaded;

    protected static void Notify() => FooTypeLoaded?.Invoke();
}

class Foo:FooBaseNotifier
{
    static Foo() => Notify();
}

这里的关键是使用 C# 静态构造函数 rules。我们使用一个抽象基 class 来定义事件和一个受保护的方法来调用它。它必须受到保护,以便 Foo 可以从静态构造函数触发事件。

正在测试:

FooBaseNotifier.FooTypeLoaded += () => Console.WriteLine("Foo");
var foo = new Foo();

Console.ReadLine();

有效!这会将 "Foo" 打印到控制台。此外,由于静态事件 是抽象基础 class 的静态成员,您甚至可以这样做:

Foo.FooTypeLoaded += () => Console.WriteLine("Foo");
var foo = new Foo();

Console.ReadLine();

并且 Foo 的静态构造函数不会被调用,但直到到达带有 new Foo() 的行!

简答:没有。

更长的答案:将处理程序放在其他 class 上。 FooHandlers.MyEvent。然后让 Foo 静态构造函数触发这些事件。您需要创建一些 FooHandlers.InvokeMyEvent 方法,因为 Foo 无法直接执行此操作。

至于“为什么”,静态构造函数在运行之前并且访问了类型的字段。一个事件有一个支持字段来保存多播委托。这意味着访问事件不可避免地会在分配事件之前触发静态构造函数(毕竟这是静态构造函数的要点)。

我通常会尽量避免使用静态构造函数,因为它们往往会导致像这样的奇怪问题......如果它们抛出任何类型的异常,那么行为就会很奇怪。