代码之家  ›  专栏  ›  技术社区  ›  Cosmos24Magic

当我不需要参数时,如何使用参数取消订阅事件操作?

  •  1
  • Cosmos24Magic  · 技术社区  · 3 年前

    也许这不是最好的标题,但我不知道该怎么说。 我有一个问题/好奇心,在任何地方都找不到更好的解决方案。

    我正在开发一个Unity游戏,假设我有一个类,在其中声明一个事件并调用它

    ClassA : MonoBehaviour
    
    public static event Action<string> LevelCompletedEvent; 
    
    public void LevelCompleted()
    {
       LevelCompletedEvent?.Invoke("Good job!");
    }
    

    我想在其他两个类中使用此事件,以侦听它。 在ClassB中,我使用参数在UI中输出消息。

    ClassB : MonoBehaviour 
    
    OnEnable(){
      ClassA.LevelCompletedEvent += DisplayMessage;
    }
    
    OnDisable(){
      ClassA.LevelCompletedEvent -= DisplayMessage;
    }
    
    private void DisplayMessage(string message)
    {
       //display the message in the UI
    }
    

    然而,在ClassC中,我不需要传入参数“message”。我只需要知道活动已经完成。

    ClassC : MonoBehaviour 
    
    OnEnable(){
      ClassA.LevelCompletedEvent += PlaySound;
    }
    
    OnDisable(){
      ClassA.LevelCompletedEvent -= PlaySound;
    }
    
    private void PlaySound()
    {
       //play a sound
    }
    

    问题是它不允许我订阅事件,因为处理程序没有相同的签名。所以我尝试使用匿名委托订阅它。

    ClassC 
    
    OnEnable(){
      ClassA.LevelCompletedEvent += delegate { PlaySound(); };
    }
    
    OnDisable(){
      ClassA.LevelCompletedEvent -= delegate { PlaySound(); };
    }
    

    看到这一点,我经常使用这种方法,但最近了解到它实际上并没有从匿名代理那里退订,我应该保留对它的引用。

    所以我改成

    ClassC 
    
    private Action<string> LevelCompletedNewDelegate;
    
    
    OnEnable(){
      LevelCompletedNewDelegate = delegate { PlaySound(); };
      ClassA.LevelCompletedEvent += LevelCompletedNewDelegate;
    }
    
    OnDisable(){
      ClassA.LevelCompletedEvent -= LevelCompletedNewDelegate;
    }
    

    现在这是可行的,但我不确定这是否是最好的方法。 我有很多事件,我真的不喜欢这样,我必须在监听器方面声明一个新的动作委托。

    有没有更好的方法?

    我发现的最简单的方法是创建一个新的操作事件并为ClassC调用它,如:

    ClassA : MonoBehaviour
    
    public static event Action<string> LevelCompletedEvent; 
    public static event Action LevelCompletedPlaySound; 
    
    public void LevelCompleted()
    {
       LevelCompletedEvent?.Invoke("Good job!"); //used by ClassB
       LevelCompletedPlaySound?.Invoke(); //used by ClassC
    }
    

    这样,我可以为每节课都安排一个活动,但我不确定这样是否更好。感觉有点多余。你觉得怎么样?

    很抱歉给你发了这么长的信息,但也许这可以帮助其他正在为此挣扎的人也能理解。

    非常感谢。

    1 回复  |  直到 3 年前
        1
  •  1
  •   Digvijaysinh Gohil    3 年前

    您可以匿名订阅事件操作,如

    ClassA.LevelCompletedEvent += message => { PlaySound(); };
    

    当你这样做的时候,你不必在 OnDisable() 。会的 Garbage Collected 在场景改变时团结一致。

    但在匿名订阅时,您必须记住,您不能 Unsubscibe 如果您必须在代码中的其他位置执行,则将其添加到事件。如果你必须退订,你必须在 Class C

    公务的 documentation 表示

    如果使用匿名,则无法轻松取消订阅活动 函数来订阅它。要在此场景中取消订阅,请返回 在订阅事件的代码中,存储匿名 函数,然后将委托添加到 事件我们建议您不要使用匿名函数来 如果您必须在某个时间取消订阅活动,请订阅活动 代码的后面一点。有关anonymous的更多信息 功能

        2
  •  0
  •   Juxant    3 年前

    我认为最理想和可读的方法是使用 discard 参数

    public class ClassC : MonoBehaviour 
    {
        private void OnEnable()
        {
            ClassA.LevelCompletedEvent += PlaySound;
        }
    
        private void OnDisable()
        {
            ClassA.LevelCompletedEvent -= PlaySound;
        }
    
        private void PlaySound(string _)
        {
            //play a sound
        }
    }