Unity跨项目JSON反序列化后类型分支判断的优化咨询
问题描述
我定义了一套包含多个数据类的数据模型,Unity项目A会把这些数据类序列化后通过TCP发送给作为调试工具的Unity项目B,两个项目只共享这套数据模型。目前在B项目里,我需要遍历所有可反序列化的类型列表,逐个尝试把收到的JSON反序列化,找到匹配的类型后重建对象。之后还要写一堆if-else判断对象类型,把数据传给对应的UI控制器。但每次数据模型增删类的时候,都要修改反序列化和类型判断的代码,稳定性差,想找更优的实现方式。
测试代码如下:
// 这段代码运行在调试工具项目B中 void Update() { if (gameData == null) return; if (Server.GetRecivedAmount() <= 0) return; var msg = Server.GetRecived().Item1; System.Type messageType = null; object deserializedMessage = null; // 遍历类型列表,尝试找到能匹配JSON的类型 foreach (System.Type t in deserializableTypes) { settings.SerializationBinder = new GeneralBinder(t); try { deserializedMessage = JsonConvert.DeserializeObject(msg, settings); Debug.Log($"反序列化成功: {deserializedMessage.GetType()}"); messageType = t; break; } catch (System.Exception e) { Debug.Log("类型不匹配: " + e); } } if (messageType == null) { Debug.LogWarning($"{name} 未找到有效消息类型"); return; } if (messageType == typeof(AgentWrapper)) { var g = deserializedMessage as AgentWrapper; if(g.type == AgentWrapper.Type.NEW) { } } // 这里会越来越多if判断,想避免这种写法 }
优化方案
1. 给消息添加类型标识,跳过遍历反序列化
现在的遍历尝试反序列化效率低还容易出问题,最直接的改进是在项目A发送消息时,把类型的唯一标识(比如类型全名、自定义枚举值)和JSON一起发送。项目B收到后,先解析标识,直接找到对应类型反序列化,不用逐个尝试。
示例实现:
- 发送端(项目A):
// 封装消息结构,包含类型标识和序列化后的JSON public class WrappedMessage { public string TypeName { get; set; } public string JsonData { get; set; } } // 发送时的处理 var data = new AgentWrapper { /* 赋值 */ }; var wrapped = new WrappedMessage { TypeName = typeof(AgentWrapper).FullName, JsonData = JsonConvert.SerializeObject(data) }; string sendMsg = JsonConvert.SerializeObject(wrapped); // 通过TCP发送sendMsg
- 接收端(项目B):
void Update() { if (gameData == null || Server.GetRecivedAmount() <= 0) return; var msg = Server.GetRecived().Item1; // 先解析外层的WrappedMessage var wrappedMsg = JsonConvert.DeserializeObject<WrappedMessage>(msg); // 根据TypeName获取对应的类型 System.Type targetType = Type.GetType(wrappedMsg.TypeName); if (targetType == null) { Debug.LogWarning($"未找到类型: {wrappedMsg.TypeName}"); return; } // 直接反序列化到目标类型 object deserializedData = JsonConvert.DeserializeObject(wrappedMsg.JsonData, targetType); // 后续处理见方案2 }
2. 用策略模式映射类型与处理逻辑,消除if-else
把每个数据类型对应的UI处理逻辑封装成独立的方法,用一个Dictionary<Type, Action<object>>来存储类型和处理函数的映射关系。初始化时注册所有类型的处理逻辑,后续新增类型只需要添加注册,不用修改Update里的核心代码。
示例实现:
// 定义全局的处理映射字典 private Dictionary<Type, Action<object>> _messageHandlers = new Dictionary<Type, Action<object>>(); void Awake() { // 初始化时注册所有类型的处理逻辑 RegisterHandler<AgentWrapper>(HandleAgentWrapper); // 新增类型时,只需要添加一行RegisterHandler即可 // RegisterHandler<NewDataClass>(HandleNewDataClass); } // 通用注册方法 private void RegisterHandler<T>(Action<T> handler) { _messageHandlers.Add(typeof(T), obj => handler((T)obj)); } // 具体的处理方法 private void HandleAgentWrapper(AgentWrapper data) { if(data.type == AgentWrapper.Type.NEW) { // 调用对应的UI控制器更新逻辑 // UIAgentController.Instance.UpdateData(data); } } // Update里的处理逻辑简化为: void Update() { // ... 前面的反序列化逻辑(用方案1的方式直接得到deserializedData和targetType) if (_messageHandlers.TryGetValue(targetType, out var handler)) { handler.Invoke(deserializedData); } else { Debug.LogWarning($"未找到类型{targetType.Name}的处理逻辑"); } }
3. 用自定义特性标记处理类(进阶)
如果项目比较复杂,可以用自定义Attribute标记负责处理某类数据的UI控制器,然后通过反射自动扫描并注册处理逻辑,完全不用手动写注册代码。
示例实现:
- 自定义特性:
[AttributeUsage(AttributeTargets.Class)] public class MessageHandlerAttribute : Attribute { public Type TargetType { get; } public MessageHandlerAttribute(Type targetType) { TargetType = targetType; } }
- UI控制器类标记特性:
[MessageHandler(typeof(AgentWrapper))] public class UIAgentController : MonoBehaviour { public void UpdateData(AgentWrapper data) { // 更新UI逻辑 } }
- 自动注册逻辑(在Awake里执行):
void Awake() { // 扫描所有带有MessageHandlerAttribute的类 var handlerTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(asm => asm.GetTypes()) .Where(t => t.GetCustomAttribute<MessageHandlerAttribute>() != null); foreach (var type in handlerTypes) { var attr = type.GetCustomAttribute<MessageHandlerAttribute>(); // 获取实例(如果是MonoBehaviour,找场景中的实例) var instance = FindObjectOfType(type); if (instance == null) continue; // 获取处理方法(假设方法名固定为UpdateData) var method = type.GetMethod("UpdateData", new[] { attr.TargetType }); if (method == null) continue; // 注册到字典 _messageHandlers.Add(attr.TargetType, obj => method.Invoke(instance, new[] { obj })); } }
这样新增数据类时,只需要给对应的UI控制器加上[MessageHandler(typeof(NewDataClass))],并实现UpdateData(NewDataClass data)方法即可,完全不用修改核心的消息处理代码。
内容的提问来源于stack exchange,提问作者dnorambu 98
相关产品推荐
相关产品推荐

