问题描述
我熟悉 C# 中的事件和委托,并且我已经使用它们很长一段时间了
// Subscribing
SomeEvent += SomeMethod;
// Unsubscribing
SomeEvent -= SomeMethod;
但是,我看到过类似以下内容
SomeEvent = SomeMethod
它与订阅事件有何不同。在上面的代码中,SomeMethod
在引发 SomeEvent
时被调用,但它与订阅事件有何不同,它在幕后做了什么?
编辑
根据@Eldar 和@PMF 的反馈,我发现 SomeEvent = SomeMethod
被称为单播委托使用或单播使用。
为了正确理解它,我在 Unity 中快速设置了一个测试脚本
public class Testing : MonoBehavIoUr
{
// UnityAction is a simple delegate
// public delegate void UnityAction();
private UnityAction OnTestAction;
private void Start()
{
OnTestAction = OnTestActionHandler;
}
private void OnTestActionHandler()
{
Debug.Log("On Test Action Handler");
}
// Update is basically infinite loop in unity
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
OnTestAction?.Invoke();
}
if (Input.GetKeyDown(KeyCode.A))
{
OnTestAction -= OnTestActionHandler;
}
}
}
在上面的代码中,我发现它与 Subscribing
和 Unsubscribing
的工作原理相同,所以我们可以使用 +=
而不是 =
还是使用 {{ 1}} 当您只需要为单个方法订阅事件时?
解决方法
注意只有 field-like events 可以这样使用:
SomeEvent = SomeMethod;
SomeEvent += SomeMethod;
和 SomeEvent -= SomeMethod;
另一方面可以用于所有事件,它们分别调用 add
的 remove
和 SomeEvent
访问器。
以下是来自语言规范的类似字段的事件:
在包含事件声明的类或结构的程序文本中,某些事件可以像字段一样使用。要以这种方式使用,事件 [...] 不得显式包含 event_accessor_declarations。此类事件可用于允许字段的任何上下文中。该字段包含一个委托,它指的是已添加到事件中的事件处理程序列表。如果未添加任何事件处理程序,则该字段包含 null。
一般来说,像 event 这样的字段没有显式的 add
和 remove
访问器。
注意他们如何有一个委托类型的字段来存储事件处理程序的列表。 SomeEvent = SomeMethod;
基本上将字段设置为一个委托,其调用列表只包含 SomeMethod
。这意味着列表中所有先前的处理程序都将被丢弃。如果您想知道如何将方法名称转换为委托,请参阅 method group conversions。
字段类事件的 add
和 remove
访问器是自动生成的:
在编译类似字段的事件时,编译器会自动创建存储来保存委托,并为事件创建访问器,为委托字段添加或删除事件处理程序。添加和删除操作是线程安全的,可以(但不是必须)在持有实例事件的包含对象或静态事件的类型对象的锁时完成。
规范并没有具体说明如何,所以这取决于实现来决定。在 SharpLab 上,我们可以看到一种实现是:
private EventHandler m_SomeEvent;
private event EventHandler SomeEvent
{
[CompilerGenerated]
add
{
EventHandler eventHandler = this.SomeEvent;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2,value);
eventHandler = Interlocked.CompareExchange(ref this.SomeEvent,value2,eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
[CompilerGenerated]
remove
{
EventHandler eventHandler = this.SomeEvent;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2,eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
}
注意字段 m_SomeEvent
以及对 Delegate.Remove
和 Delegate.Combine
的调用 - 这些是将新处理程序添加到处理程序列表并从处理程序列表中删除处理程序的原因.
那么我们可以使用 += 代替 = 吗,或者当您只需要为单个方法订阅事件时使用 += 是否不好?
当您只订阅一种方法时使用 +=
也不错。事实上,我会推荐您使用+=
。你可能会说“我只想订阅一个方法”现在,但你永远不知道什么时候会改变:)