如何在不调用静态构造函数的情况下将处理程序绑定到静态事件?
How can I bind a handler to a static event without invoking the static constructor?
恐怕我知道这个问题的答案但是...
是否可以成功绑定到将在静态构造函数期间引发的静态事件?或者这在逻辑上是不可能的?
我的事件处理程序没有被命中。我怀疑是因为当我这样做时...
MyClass.MyEvent += MyEventHandler;
...对 MyClass
的调用是 运行 静态构造函数,因此在绑定处理程序时,该构造函数内的事件已经引发,稍后在该行代码中.
对吗?还有另一种方法吗?是否可以在不导致静态构造函数执行的情况下绑定到静态事件?
当您使用 event
关键字声明一个事件时,您实际上是在声明两个方法,add
和 remove
。把它想象成当你声明一个 属性 时:在幕后,你实际上是在声明一个 set
和一个 get
方法。事件也不例外。您实际上可以在代码 .
中覆盖 add
和 remove
所以当你打电话时
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 无法直接执行此操作。
至于“为什么”,静态构造函数在运行之前并且访问了类型的字段。一个事件有一个支持字段来保存多播委托。这意味着访问事件不可避免地会在分配事件之前触发静态构造函数(毕竟这是静态构造函数的要点)。
我通常会尽量避免使用静态构造函数,因为它们往往会导致像这样的奇怪问题......如果它们抛出任何类型的异常,那么行为就会很奇怪。
恐怕我知道这个问题的答案但是...
是否可以成功绑定到将在静态构造函数期间引发的静态事件?或者这在逻辑上是不可能的?
我的事件处理程序没有被命中。我怀疑是因为当我这样做时...
MyClass.MyEvent += MyEventHandler;
...对 MyClass
的调用是 运行 静态构造函数,因此在绑定处理程序时,该构造函数内的事件已经引发,稍后在该行代码中.
对吗?还有另一种方法吗?是否可以在不导致静态构造函数执行的情况下绑定到静态事件?
当您使用 event
关键字声明一个事件时,您实际上是在声明两个方法,add
和 remove
。把它想象成当你声明一个 属性 时:在幕后,你实际上是在声明一个 set
和一个 get
方法。事件也不例外。您实际上可以在代码
add
和 remove
所以当你打电话时
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 无法直接执行此操作。
至于“为什么”,静态构造函数在运行之前并且访问了类型的字段。一个事件有一个支持字段来保存多播委托。这意味着访问事件不可避免地会在分配事件之前触发静态构造函数(毕竟这是静态构造函数的要点)。
我通常会尽量避免使用静态构造函数,因为它们往往会导致像这样的奇怪问题......如果它们抛出任何类型的异常,那么行为就会很奇怪。