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

如何在C#中序列化存储在变量中的回调函数?

序列化与反序列化Action回调的解决方案

直接序列化Action<object>委托存在诸多限制(如依赖目标对象可序列化、方法签名/类型稳定性要求高等),我们需要通过序列化方法元数据+目标对象的方式间接实现,以下是具体实现方案:

核心思路

  1. 序列化时:区分回调是静态方法还是实例方法,分别保存类型全名/方法名(静态)或目标对象/方法名(实例),同时标记是否为null
  2. 反序列化时:根据保存的元数据重新查找方法,绑定到目标对象(实例方法)或直接创建静态委托,重构出可用的Action<object>引用

修改后的代码实现

升级序列化版本并添加回调序列化逻辑

public class Event
{
    // 原有成员变量保持不变
    private TimeSpan m_Offset = TimeSpan.Zero;              
    private TimeSpan m_Duration = TimeSpan.Zero;            
    private TimeSpan m_Countdown = TimeSpan.Zero;           
    private int m_OffsetFromUTC = -8;                       
    DateTime m_EventStart, m_EventEnd;                      
    private Server.Timer m_Timer;                           
    private bool m_Calledstart = false;                     
    private Item m_item;                                    
    private object m_context;                              
    private Action<object> m_startedCallback;               
    private Action<object> m_endedCallback;                 

    public Event(Item you, object context = null, Action<object> startedCallback = null, Action<object> endedCallback = null)
    {
        m_item = you;
        m_context = context;
        m_startedCallback = startedCallback;
        m_endedCallback = endedCallback;
    }

    public void Serialize(GenericWriter writer)
    {
        writer.Write((int)2);   // 版本升级为2,新增回调序列化逻辑
        writer.Write(DateTime.Now);
        writer.Write(m_Duration);
        writer.Write(m_Calledstart);
        writer.Write(m_OffsetFromUTC);
        writer.Write(m_Offset);
        
        // 序列化两个回调
        SerializeAction(writer, m_startedCallback);
        SerializeAction(writer, m_endedCallback);
    }

    private void SerializeAction(GenericWriter writer, Action<object> action)
    {
        if (action == null)
        {
            writer.Write(false); // 标记回调为null
            return;
        }

        writer.Write(true);
        var method = action.Method;
        writer.Write(method.IsStatic);

        if (method.IsStatic)
        {
            // 静态方法:保存类型全名和方法名
            writer.Write(method.DeclaringType.FullName);
            writer.Write(method.Name);
        }
        else
        {
            // 实例方法:保存目标对象(需可序列化)和方法名
            writer.Write(action.Target);
            writer.Write(method.Name);
        }
    }

    public void Deserialize(GenericReader reader)
    {
        int version = reader.ReadInt();
        switch (version)
        {
            case 2:
                {
                    // 读取原有版本1的内容
                    DateTime temp = reader.ReadDateTime();
                    m_Countdown = DateTime.Now - temp;
                    m_Duration = reader.ReadTimeSpan();
                    m_Calledstart = reader.ReadBool();
                    m_OffsetFromUTC = reader.ReadInt();
                    m_Offset = reader.ReadTimeSpan();
                    
                    // 反序列化回调
                    m_startedCallback = DeserializeAction(reader);
                    m_endedCallback = DeserializeAction(reader);
                    break;
                }
            case 1:
                {
                    DateTime temp = reader.ReadDateTime();
                    m_Countdown = DateTime.Now - temp;
                    m_Duration = reader.ReadTimeSpan();
                    m_Calledstart = reader.ReadBool();
                    m_OffsetFromUTC = reader.ReadInt();
                    m_Offset = reader.ReadTimeSpan();
                    // 旧版本无回调,默认赋值null
                    m_startedCallback = null;
                    m_endedCallback = null;
                    break;
                }
        }
    }

    private Action<object> DeserializeAction(GenericReader reader)
    {
        bool hasAction = reader.ReadBool();
        if (!hasAction)
        {
            return null;
        }

        bool isStatic = reader.ReadBool();
        if (isStatic)
        {
            string typeName = reader.ReadString();
            string methodName = reader.ReadString();

            Type type = Type.GetType(typeName);
            if (type == null) return null;

            // 查找匹配签名的静态方法
            MethodInfo method = type.GetMethod(methodName, 
                BindingFlags.Public | BindingFlags.Static, 
                null, new[] { typeof(object) }, null);
            if (method == null) return null;

            return (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), method);
        }
        else
        {
            object target = reader.ReadObject();
            string methodName = reader.ReadString();

            if (target == null) return null;

            Type targetType = target.GetType();
            // 先找公共实例方法,找不到再尝试私有方法
            MethodInfo method = targetType.GetMethod(methodName, 
                BindingFlags.Public | BindingFlags.Instance, 
                null, new[] { typeof(object) }, null) 
                ?? targetType.GetMethod(methodName, 
                    BindingFlags.NonPublic | BindingFlags.Instance, 
                    null, new[] { typeof(object) }, null);

            if (method == null) return null;

            return (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), target, method);
        }
    }
}

关键注意事项

  • 目标对象可序列化:实例方法的目标对象(如Item类)必须标记[Serializable]特性,否则序列化会失败
  • 避免匿名方法/Lambda:匿名方法和Lambda由编译器生成动态类型,无法稳定序列化,建议使用具名方法作为回调
  • 方法签名严格匹配:回调方法必须是void 方法名(object 参数)的签名,否则反序列化时无法找到对应方法
  • 版本兼容性:如果后续修改类型名、方法名或程序集版本,需额外处理兼容性逻辑,避免反序列化失败

内容的提问来源于stack exchange,提问作者Level 42

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 20:30:56