Unity中如何用JsonUtility.ToJson序列化及反序列化System.Action?
解决Unity中JsonUtility序列化/反序列化System.Action的问题
Unity自带的JsonUtility无法直接序列化或反序列化System.Action委托——因为委托本质是方法的引用,不属于可被JSON存储的数值或结构化数据类型。要实现这个需求,必须通过间接存储方法标识信息+反射重建委托的方式来处理。
解决方案步骤
1. 修改EnvironmentAction类,添加序列化支撑逻辑
新增可序列化字段存储方法的类名和方法名,同时通过ISerializationCallbackReceiver接口处理序列化前后的逻辑转换:
using UnityEngine; using System; using System.Reflection; [Serializable] public class EnvironmentAction : ISerializationCallbackReceiver { // 用于JSON序列化的字段:存储方法所属类的全名、方法名 [SerializeField] private string _className; [SerializeField] private string _methodName; // 实际业务使用的委托,标记为NonSerialized避免JsonUtility处理 [NonSerialized] private Action _actionToDoNoParameters; // 构造函数 public EnvironmentAction(Action actionToDo) { _actionToDoNoParameters = actionToDo; } public void Execute() { _actionToDoNoParameters?.Invoke(); } // 序列化前回调:从委托中提取方法标识信息 public void OnBeforeSerialize() { if (_actionToDoNoParameters == null) { _className = null; _methodName = null; return; } MethodInfo method = _actionToDoNoParameters.Method; _className = method.DeclaringType.FullName; _methodName = method.Name; } // 反序列化后回调:通过反射重建委托 public void OnAfterDeserialize() { if (string.IsNullOrEmpty(_className) || string.IsNullOrEmpty(_methodName)) { _actionToDoNoParameters = null; return; } // 获取目标类型 Type targetType = Type.GetType(_className); if (targetType == null) { Debug.LogError($"找不到目标类型:{_className}"); return; } // 获取目标方法(包含公共、私有、静态、实例方法) MethodInfo method = targetType.GetMethod(_methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); if (method == null) { Debug.LogError($"类型 {_className} 中找不到方法:{_methodName}"); return; } // 重建委托:区分静态方法和实例方法 if (method.IsStatic) { _actionToDoNoParameters = (Action)Delegate.CreateDelegate(typeof(Action), method); } else { // 实例方法需要获取对应的对象实例,这里以单例为例 // 你可以根据实际场景调整实例获取逻辑(比如通过InstanceID查找场景对象) object instance = targetType.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static)?.GetValue(null); if (instance == null) { Debug.LogError($"无法获取 {_className} 的实例,无法绑定实例方法 {_methodName}"); return; } _actionToDoNoParameters = (Action)Delegate.CreateDelegate(typeof(Action), instance, method); } } }
2. 核心逻辑说明
- 序列化阶段:调用
JsonUtility.ToJson()前,OnBeforeSerialize自动触发,从委托中提取目标方法的类全名和方法名,存储到可序列化的字符串字段中,这些字段会被正常序列化为JSON内容。 - 反序列化阶段:调用
JsonUtility.FromJson()后,OnAfterDeserialize自动触发,通过反射根据存储的类名和方法名找到对应的方法,重新创建委托绑定到_actionToDoNoParameters字段。
3. 注意事项与限制
- 匿名方法/Lambda表达式无法序列化:这类方法没有固定的方法名,反射无法定位,必须使用具名的静态或实例方法。
- 实例方法的实例获取:如果绑定的是实例方法,必须确保反序列化时能获取到对应的对象实例。比如单例类可以通过单例属性获取;场景中的MonoBehaviour可以保存对象的
InstanceID,反序列化时用FindObjectOfInstanceID查找。 - 方法访问权限:反射时指定了
BindingFlags.NonPublic,支持序列化私有方法。
测试代码示例
using UnityEngine; using System.IO; public class ActionSerializationTest : MonoBehaviour { private EnvironmentAction _environmentAction; void Start() { // 绑定静态方法 _environmentAction = new EnvironmentAction(TestStaticMethod); // 绑定单例实例方法示例 // _environmentAction = new EnvironmentAction(TestSingleton.Instance.TestInstanceMethod); } private static void TestStaticMethod() { Debug.Log("执行静态测试方法"); } private void OnApplicationQuit() { // 序列化到JSON文件 string json = JsonUtility.ToJson(_environmentAction); File.WriteAllText(@".\test_action.json", json); // 反序列化测试 EnvironmentAction deserializedAction = JsonUtility.FromJson<EnvironmentAction>(json); deserializedAction.Execute(); // 执行后会输出对应的日志 } } // 单例示例类 public class TestSingleton { private static TestSingleton _instance; public static TestSingleton Instance => _instance ??= new TestSingleton(); public void TestInstanceMethod() { Debug.Log("执行单例实例测试方法"); } }
内容的提问来源于stack exchange,提问作者nathan raynal
相关产品推荐
相关产品推荐

