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

Unity项目中泛型与非泛型UnityEvent集合实现技术咨询

Solution for Unifying Non-Generic and Generic ItemAction in Unity Item Class

Hey there! Let's break down how to solve this problem neatly. Since you've got two flavors of ItemAction—one wrapping a plain UnityEvent and another wrapping UnityEvent<T>—the key is to give them a common base type so your abstract Item class can hold both in a single collection. Here's a step-by-step implementation:

1. Create a Shared Abstract Base Class

First, define an abstract base class that both ItemAction variants will inherit from. This gives us a unified type to use in our collection, plus core functionality for invoking actions and type safety checks:

using System;
using UnityEngine;

public abstract class BaseItemAction : ScriptableObject // Remove ScriptableObject if your actions are plain C# classes
{
    // Returns the required parameter type (null for non-generic actions)
    public abstract Type ParameterType { get; }

    // Generic invoke method to handle both non-generic and generic cases
    public abstract void Invoke(object parameter = null);
}

2. Update Your Existing ItemAction Classes

Modify your non-generic and generic ItemAction classes to inherit from BaseItemAction, implementing the required methods to fit the unified interface:

Non-Generic ItemAction (Wraps UnityEvent)

using UnityEngine;
using UnityEngine.Events;

public class ItemAction : BaseItemAction
{
    [SerializeField] private UnityEvent _onTriggered;

    public override Type ParameterType => null;

    public override void Invoke(object parameter = null)
    {
        if (parameter != null)
        {
            Debug.LogWarning("Non-generic ItemAction received an unexpected parameter; ignoring it.");
        }
        _onTriggered?.Invoke();
    }

    // Optional: Helper method for cleaner parameterless calls
    public void Invoke() => _onTriggered?.Invoke();
}

Generic ItemAction<T> (Wraps UnityEvent<T>)

using System;
using UnityEngine;
using UnityEngine.Events;

public class ItemAction<T> : BaseItemAction
{
    [SerializeField] private UnityEvent<T> _onTriggered;

    public override Type ParameterType => typeof(T);

    public override void Invoke(object parameter = null)
    {
        if (parameter is T typedParameter)
        {
            _onTriggered?.Invoke(typedParameter);
        }
        else if (parameter != null)
        {
            Debug.LogWarning($"Invalid parameter type for ItemAction<{typeof(T).Name}>: expected {typeof(T).Name}, got {parameter.GetType().Name}");
        }
        else
        {
            Debug.LogWarning($"ItemAction<{typeof(T).Name}> requires a parameter of type {typeof(T).Name}, but received null.");
        }
    }

    // Optional: Strongly-typed helper method for safer, cleaner calls
    public void Invoke(T parameter) => _onTriggered?.Invoke(parameter);
}

3. Add the Unified Collection to Your Abstract Item Class

Now you can use a List<BaseItemAction> in your Item class to hold both types of actions. We'll also add flexible helper methods to trigger actions in different scenarios:

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

public abstract class Item : ScriptableObject // Adjust if your Item is a plain C# class
{
    [SerializeField] private List<BaseItemAction> _itemActions = new List<BaseItemAction>();

    // Trigger all actions (non-generic run, generic ones warn if no parameter is passed)
    public void TriggerAllActions()
    {
        foreach (var action in _itemActions)
        {
            action.Invoke();
        }
    }

    // Trigger all actions with a parameter (only matching generic actions run correctly)
    public void TriggerAllActions<T>(T parameter)
    {
        foreach (var action in _itemActions)
        {
            action.Invoke(parameter);
        }
    }

    // Trigger only non-generic actions
    public void TriggerNonGenericActions()
    {
        foreach (var action in _itemActions.OfType<ItemAction>())
        {
            action.Invoke();
        }
    }

    // Trigger only generic actions of a specific type
    public void TriggerGenericActions<T>(T parameter)
    {
        foreach (var action in _itemActions.OfType<ItemAction<T>>())
        {
            action.Invoke(parameter);
        }
    }

    // Smart trigger: Automatically match parameters to compatible action types
    public void TriggerActionsWithParameter(object parameter)
    {
        if (parameter == null)
        {
            TriggerNonGenericActions();
            return;
        }

        foreach (var action in _itemActions)
        {
            if (action.ParameterType == null)
            {
                action.Invoke();
            }
            else if (action.ParameterType.IsAssignableFrom(parameter.GetType()))
            {
                action.Invoke(parameter);
            }
            else
            {
                Debug.LogWarning($"Skipping action {action.name} - parameter type mismatch.");
            }
        }
    }
}

Key Notes

  • Type Safety: The strongly-typed Invoke(T) method in ItemAction<T> lets you call generic actions directly without casting, while the base Invoke(object) handles unified collection calls.
  • Debugging: Built-in warning messages help catch mismatched parameters or missing inputs during development.
  • Extensibility: You can add more functionality to the BaseItemAction class (like enabling/disabling actions) that both variants will inherit automatically.

内容的提问来源于stack exchange,提问作者J. Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:31:22