首页 > 解决方案 > 在类之间传递函数作为参数

问题描述

我有两个基于 Unity 的类,其中一个代表 Player 对象,另一个代表应该能够操纵玩家的 powerup。

我的想法是将播放器操作封装在一个可以传递给播放器对象的方法中,例如当播放器与电源碰撞时。

这些操作可以包括简单的字段和属性更改(健康点),也可能包括协程(自动射击或具有时间间隔的盾牌)。

目前,我的播放器类在其自身中包含所有与上电相关的操作,并根据碰撞时从上电对象传递的字符串调用它们:

播放器控制器.cs

public class PlayerController : MonoBehaviour
{
    private Dictionary<string, UnityAction> powerupMethods;

    // ...

    public void ActivatePowerUp(string name)
    {
        Invoke(powerUpMethods[name]);
    }
}

上电.cs

public class Powerup : MonoBehaviour
{
   private string _name;

   // ...

   private void OnCollision2D(Collision col)
   {
       PlayerController p = col.collider.GetComponent<PlayerController>();
       if (p != null)
       {
           p.ActivatePowerup(_name);
       }
   }
}

我的愿望是从玩家对象中消除所有与通电相关的逻辑,并将其保留在相应的通电中。

是否有技术可能性将此类操作包含在可以从 powerup 对象传递到 player 对象的方法中,像这样?最好的情况是玩家对象可以访问方法中的私有字段。

// Powerup.cs
public void Ability()
{
    // Do stuff with Player object, even private fields
    // e.g. player._health++;
    // But also activate self-based Coroutines
    // e.g. StartCoroutine(CoroutineMethod());
}

private IEnumerator CoroutineMethod()
{
    // e.g. auto fire for 5 seconds
}

private void OnCollision2D(Collision2D col)
{
    PlayerController p = col.collider.GetComponent<PlayerController>();
    if (p != null) p.Invoke(Ability());
}

标签: c#functionunity3dparameter-passing

解决方案


首先让我说我认为传递对方法的引用是这种情况下的错误解决方案。我只需将 的引用传递给类Player中的方法Powerup

话虽如此,使用委托是 .Net 编程的一个重要而强大的部分,所以我想我最好给你一些介绍 - 所以这里是:

使用委托传递对方法的引用是可能的,甚至非常容易。从 .Net 2.0 版本开始,您可以为此使用ActionFunc预定义系统委托。
它们有很多,从无参数到 16 个通用参数不等,这意味着您很少需要像我们在 .Net 1.1 中那样以老式方式编写委托。(很久很久以前)

所有Action委托代表void方法,所有Func委托代表返回值的方法。Action创建一个接受 an或 a作为参数并调用它的方法非常容易(也是一种非常常见的做法)Func- .Net 框架的许多内置类和扩展方法都是这样做的,例如List<T>.ForEachlinq 方法是几乎完全基于Func代表。

让我们以 linq to objectAny()方法源代码为例(因为它很简单):

public static bool Any<TSource>(this IEnumerable<TSource> source, 
                                Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

如您所见,它接受 aFunc<TSource, bool>并在 中的每个项目上调用它,IEnumerable直到找到谓词返回的true关于它的项目,或者它到达 a 的末尾IEnumerable并返回false


推荐阅读