[Windows翻译]Windows Runtime委托和C++/WinRT中的对象寿命。

245 阅读4分钟

原文地址:devblogs.microsoft.com/oldnewthing…

原文作者:devblogs.microsoft.com/oldnewthing…

发布时间:2019年5月24日

在C++/WinRT中,有四种方法可以为事件处理程序创建委托。

  • 作为一个带有方法指针的原始指针。
  • 作为一个强指针与一个方法指针。
  • 作为一个弱指针与一个方法指针。
  • 作为一个lambda。
// raw pointer with method pointer.
MyButton.Event({ this, &AwesomePage::Button_Click });

// strong pointer with method pointer.
MyButton.Event({ get_strong(), &AwesomePage::Button_Click });

// weak pointer with method pointer.
MyButton.Event({ get_weak(), &AwesomePage::Button_Click });

// lambda.
MyButton.Event([...](auto&& sender, auto&& args) { ... });

前三个都非常相似。它们调用对象上的方法。唯一不同的是它们如何获取对象。

  • 原始指针。方法是在原始指针上调用的。
  • 强指针:方法被调用在强指针上。方法被调用在强指针上。
  • 弱指针:弱指针被解析成强指针。弱指针被解析为强指针。如果成功,该方法被调用到强指针上。如果不成功,则什么都不会发生¹

lambda的情况与以往相同。lambda被调用。由lambda决定什么对象被捕获,如何捕获,以及当lambda被调用时会发生什么。

棘手的部分是决定哪种机制最适合你的用例。

如果你需要由强指针引用的对象在事件处理程序注册期间一直保持活力,那么就使用强指针。这不是一个常见的场景;通常,你是通过其他方式来保持对象的活力。

在讨论使用弱指针或原始指针的情况之前,我们先想想这些选项要解决的问题。

根本的问题是对象被破坏后引发的事件。你能保证这种情况不会发生吗?如果你能保证,那么你可以使用一个原始指针。如果你不能保证,那么你必须使用一个弱指针。如果你不确定,使用弱指针总是可以的)。

在什么情况下,你可以保证在对象被破坏后事件不会被引发呢?一个显而易见的要求是,你必须阻止事件处理程序被调用。标准的方法是取消注册事件处理程序。一个不太常见的方法是销毁事件源。(之所以不那么常见,是因为它假定没有人对事件源进行引用,从而使它保持活力!)

防止事件源调用你的处理程序完全是你的责任,所以我们假设你记得这样做,而且你做得正确。

即使你记得取消注册事件处理程序,如果事件可以从另一个线程中引发,那么取消注册后也有可能接收到一个事件,这有一个不可避免的竞赛条件,因为在你取消注册处理程序时,事件可能正在飞行中。唯一能保证你在取消注册后不会收到任何事件的方法是,事件总是从进行取消注册的线程中引发。这种保证只适用于具有线程亲和力的对象,以及在事件触发时同步引发事件的情况下。在实践中,这意味着你只有对UI对象,比如XAML元素,才有这个保证。

好了,现在我们了解了这些场景,我们可以写指导意见了。

如果事件源具有线程亲和力(典型的UI对象),那么你可以选择使用弱指针或生指针版本。原始指针的效率更高,但这要看你是否做了正确的分析。当然,当你的对象被销毁时,你必须记得停止事件处理程序的调用。

在其他所有你要在弱指针和原始指针之间选择的情况下,使用弱指针,即如果事件源不具有线程亲和力,基本上意味着事件源不是UI对象。

当通过XAML标记创建事件处理程序时,产生的委托是用原始指针和方法指针创建的。

<!-- XAML -->
<Page x:Name="AwesomePage" ...>
  ...
  <Button Click="Button_Click" >
  ...
</Page>

当您编写上述XAML时,创建委托就像您编写了

thatButton.Click({ this, &AwesomePage::Button_Click });

XAML编译器知道这样做是安全的,因为它把处理程序挂到了一个XAML元素上,而这个元素是一个具有线程亲和力的UI对象。因此,它知道它可以在销毁时取消注册它的处理程序,而不会有事件迟到的风险。

补充阅读:

¹ 请注意,"什么都没有发生 "也意味着它甚至没有返回神奇的RPC_E_DISCONNECTED错误代码来告诉事件源该处理程序已经永久死亡


www.deepl.com 翻译