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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 04:57:08