You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否使用Func的Invoke方法返回对象数组?(游戏工具开发场景)

解决方案:收集多播委托所有返回值并减少内存分配

原代码核心问题

  1. 委托签名不匹配:Func<int, int[]>要求接收int参数并返回int[],但你的RandomNumber是无参方法,编译会直接报错。
  2. 多播委托的局限:直接调用多播委托的Invoke()只会返回最后一个注册方法的结果,无法收集所有订阅者的返回值。

实现思路

  • 用Func<int>作为订阅委托的类型(无参返回单个int),匹配取值方法签名。
  • 通过遍历多播委托的InvocationList,逐个调用订阅方法并收集结果。
  • 加入缓存机制或对象池,减少频繁创建数组带来的内存分配,适配游戏开发的性能需求。

基础实现(带缓存)

using UnityEngine;
using System;

public class ValueCollector : MonoBehaviour
{
    // 用event封装委托,限制外部只能通过订阅/取消订阅操作
    private event Func<int> _valueProviders;
    // 缓存结果,仅在订阅变化时重新生成
    private int[] _cachedValues;

    // 外部订阅接口
    public void Subscribe(Func<int> valueProvider)
    {
        _valueProviders += valueProvider;
        // 订阅变更,清空缓存
        _cachedValues = null;
    }

    // 外部取消订阅接口
    public void Unsubscribe(Func<int> valueProvider)
    {
        _valueProviders -= valueProvider;
        _cachedValues = null;
    }

    // 获取所有订阅者的返回值数组
    public int[] GetAllValues()
    {
        // 返回缓存结果,避免重复分配
        if (_cachedValues != null)
            return _cachedValues;

        // 无订阅时返回空数组
        if (_valueProviders == null)
            return Array.Empty<int>();

        // 遍历所有订阅的方法,收集结果
        var invocationList = _valueProviders.GetInvocationList();
        _cachedValues = new int[invocationList.Length];

        for (int i = 0; i < invocationList.Length; i++)
        {
            var provider = (Func<int>)invocationList[i];
            _cachedValues[i] = provider.Invoke();
        }

        return _cachedValues;
    }

    // 测试用取值方法
    private int RandomNumber()
    {
        return UnityEngine.Random.Range(0, 10);
    }

    private void Start()
    {
        // 订阅多个取值方法
        Subscribe(RandomNumber);
        Subscribe(() => 5);
        Subscribe(() => 15);

        // 获取并输出结果
        var values = GetAllValues();
        foreach (var num in values)
        {
            Debug.Log(num);
        }
    }
}

进阶优化:用对象池减少内存分配

如果需要频繁调用GetAllValues(),可以用数组对象池复用数组,避免频繁创建销毁带来的GC:

using UnityEngine;
using System;
using System.Collections.Generic;

public class ValueCollector : MonoBehaviour
{
    private event Func<int> _valueProviders;
    // 数组对象池实现
    private static readonly Queue<int[]> _arrayPool = new Queue<int[]>();

    public void Subscribe(Func<int> valueProvider)
    {
        _valueProviders += valueProvider;
    }

    public void Unsubscribe(Func<int> valueProvider)
    {
        _valueProviders -= valueProvider;
    }

    public int[] GetAllValues()
    {
        if (_valueProviders == null)
            return Array.Empty<int>();

        var invocationList = _valueProviders.GetInvocationList();
        int length = invocationList.Length;
        
        // 从池子里租数组,没有则新建
        int[] values = _arrayPool.Count > 0 && _arrayPool.Peek().Length >= length 
            ? _arrayPool.Dequeue() 
            : new int[length];

        // 收集所有返回值
        for (int i = 0; i < length; i++)
        {
            var provider = (Func<int>)invocationList[i];
            values[i] = provider.Invoke();
        }

        return values;
    }

    // 使用完数组后归还到池子里
    public static void ReturnArray(int[] array)
    {
        if (array == null) return;
        _arrayPool.Enqueue(array);
    }

    private int RandomNumber()
    {
        return UnityEngine.Random.Range(0, 10);
    }

    private void Start()
    {
        Subscribe(RandomNumber);
        Subscribe(() => 5);

        var values = GetAllValues();
        foreach (var num in values)
        {
            Debug.Log(num);
        }
        // 使用完归还数组
        ReturnArray(values);
    }
}

关键说明

  • 模块化组织:通过Subscribe/Unsubscribe接口,其他脚本可以灵活注册取值逻辑,实现解耦。
  • 内存优化:缓存机制适合取值不频繁的场景,对象池适合高频调用场景,都能有效减少不必要的内存分配。
  • 类型安全:用event封装委托,避免外部直接修改委托列表,保证代码稳定性。

内容的提问来源于stack exchange,提问作者André Santana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:11:05