Unity事件处理程序

0 c# unity-game-engine

我是 c# 和 unity 的新手,想要使用 EventHandler 来宣布其他脚本来执行某些操作。

一些代码已订阅此事件。

“if(RefreshLevel != null)”到底是做什么的,“RefreshLevel”的内容是什么,为什么这个事件没有被触发?

using System;
using UnityEngine;

public class GameLevel : MonoBehaviour
{
  public static GameLevel current;

  private void Awake()
  {
    current = this;
  }

  private int level = 1;

  private int manyItem;
  private int burnedItem = 0;

  public event EventHandler<LevelEventArgs> RefreshLevel;

  // Start is called before the first frame update
  void Start()
  {
    itemWorld = GameObject.Find("ItemWorld");
    manyItem = itemWorld.transform.childCount;
  }

  public void LevelUp()
  {
    burnedItem += 1;

    if (burnedItem < manyItem)
    {
      level += 1;

      if(RefreshLevel != null)
      {
        RefreshLevel(this, new LevelEventArgs(level));
      }

      Debug.Log("Burned Item: " + burnedItem);
      Debug.Log("Level: " + level);
    }
    else if(burnedItem == manyItem)
    {
      Debug.Log("Burned Item: " + burnedItem);
      Debug.Log("Ghost Dead");
    }
  }
}

public class LevelEventArgs : EventArgs
{
  public LevelEventArgs(int level)
  {
    Level = level;
  }

  public int Level;
}
Run Code Online (Sandbox Code Playgroud)

der*_*ugo 6

长话短说

if(RefreshLevel != null)
Run Code Online (Sandbox Code Playgroud)

除了检查是否有人“监听”该事件之外什么也不做。只要没有人将侦听器/回调附加到事件(调用列表为空),它就等于null并且调用它会抛出NullReferenceException.

您也可以将其写为

RefreshLevel?.Invoke(this, new LevelEventArgs(level));
Run Code Online (Sandbox Code Playgroud)

a) 写起来更短,b) 更清楚地表明这是一个事件而不是一个正常的方法。


背景

EventHandler<T>

公共委托 void EventHandler(object? sender, EventArgs e);

只是一个delegate,表示方法签名的模板(类似于类的接口)

然后脚本将其用作event具有特殊含义的

事件是一种特殊类型的多播委托,只能从声明它们的类或结构(发布者类)中调用。如果其他类或结构订阅了该事件,则当发布者类引发该事件时,将调用它们的事件处理程序方法。有关更多信息和代码示例,请参阅事件和委托。

而在它的背后有一个MulticastDelegate

代表多播委托;也就是说,一个委托在其调用列表中可以有多个元素。

该“调用列表”是注册的回调。

最后是返回的运算符MulticastDelegate.Inequality

如果 d1 和 d2 没有相同的调用列表,则为 true;否则为假

如果两个委托不同且类型完全相同,则它们相等null它们的调用列表包含相同数量的元素,并且第一个委托的调用列表中的每个元素都等于第二个委托的调用列表中的相应元素代表。

因此,如果您将 aneventnull它进行比较,只要调用列表中没有元素,它就是 true。


进一步说明

一些代码已订阅此事件。

请允许我声明这不是真的。如果您的事件从未被调用(但满足其他条件),则意味着您没有在任何地方注册该事件的任何回调,例如

private void Start()
{
    yourGameLevel.RefreshLevel += OnGameLevelRefreshed;
}

private void OnGameLevelRefreshed(object sender, LevelEventArgs args)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

或者它可能只是意味着GameLevel您注册回调的实例与您正在查看的实例不同。

如果这能够改变(假设由于这件事GameLevel.current),您可能更想举办您的活动static,因为无论如何您都会传递发件人的参考,以防有人需要它。

public static event EventHandler<LevelEventArgs> RefreshLevel;
Run Code Online (Sandbox Code Playgroud)

然后宁愿走

private void Start()
{
    GameLevel.RefreshLevel += OnGameLevelRefreshed;
}

private void OnGameLevelRefreshed(object sender, LevelEventArgs args)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这样您就可以确保即使当前实例被更改/销毁,您仍然会收到任何调用的事件。