【问题标题】:Serialize classes, that contain non serializable members序列化包含不可序列化成员的类
【发布时间】:2015-07-20 13:08:21
【问题描述】:

是否有可能对包含不可序列化成员的类进行序列化?

示例: 我有一个类指令,它有两个成员:

public class Instruction
{
    public OpCode Op { get; set; }
    public object Operand { get; set; } 
}

问题是,成员 Op 的类型在 3rd 方库中,我无法使其可序列化。我尝试了 BinaryFormatter 和 ProtoBuf,但都失败了,没有标记 Op-member。

任何人都知道序列化我的对象的解决方案吗?

【问题讨论】:

    标签: c# serialization


    【解决方案1】:

    显然,如果类的一部分不可序列化,则不能序列化该部分,但可以忽略它(并在反序列化时提供一些默认值,可能是 null 值)。

    如果您使用ProtoBuf,您可以使用[ProtoIgnore] 属性来标记Op 属性,或者BinaryFormatter 使用[NonSerialized] 属性。

    【讨论】:

    • 但是当我需要会员以供以后使用时怎么办?
    • 好吧,你不能序列化不可序列化的东西。如果不对类进行逆向工程并查看如何强制序列化,您将无能为力。也许OpCode 正在使用本机资源?
    【解决方案2】:

    一种方法是在 Opcode 上创建一个包装器类,并在指令类中使用此包装器类的对象在应用程序中进行序列化和其他用途。通过这种方式,您可以摆脱第三方库的任何限制。

    【讨论】:

      【解决方案3】:

      你可以:

      public class Instruction
      {
          public OpCode Op { get; set; }
      
          public string OpString
          {
              get
              {
                  return Op.Name;
              }
      
              set
              {
                  Op = (OpCode)typeof(OpCodes).GetField(value, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase).GetValue(null);
              }
          }
      
          public object Operand { get; set; }
      }
      

      并禁用Op 的序列化。这样你就可以序列化操作码的Name(一个string),然后你就可以反序列化它了。

      使用BinaryFormatter,您可以使用ISerializable 接口并手动序列化:

      [Serializable]
      public class Instruction : ISerializable 
      {
          public OpCode Op { get; set; }
      
          public object Operand { get; set; }
      
          public Instruction()
          {
          }
      
          public Instruction(SerializationInfo info, StreamingContext context)
          {
              Op = (OpCode)typeof(OpCodes).GetField(info.GetString("Op"), BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase).GetValue(null);
              Operand = info.GetValue("Operand", typeof(object));
          }
      
          public void GetObjectData(SerializationInfo info, StreamingContext context)
          {
              info.AddValue("Op", Op.Name);
              info.AddValue("Operand", Operand);
          }
      }
      

      使用示例:

      var ins1 = new Instruction { Op = OpCodes.Add, Operand = (short)5 };
      var ins2 = new Instruction { Op = OpCodes.Sub, Operand = 5.0 };
      
      byte[] bytes;
      
      using (var ms = new MemoryStream())
      {
          var bf = new BinaryFormatter();
          bf.Serialize(ms, ins1);
          bf.Serialize(ms, ins2);
          bytes = ms.ToArray();
      }
      
      Instruction ins3, ins4;
      
      using (var ms = new MemoryStream(bytes))
      {
          var bf = new BinaryFormatter();
          ins3 = (Instruction)bf.Deserialize(ms);
          ins4 = (Instruction)bf.Deserialize(ms);
      }
      

      如果Operand 可以是不能直接序列化的东西,那么您可以在GetObjectData 中创建它的代理项。

      【讨论】:

      • 这是 OpCode 的一种可能方式,但它不适用于对象,对吧?我的意思是,操作数可以有多种类型。
      • @user 查看扩展响应。遗憾的是 protobuf 不支持在 object 中序列化原始类型(intshort,...)
      猜你喜欢
      • 2010-10-04
      • 2016-05-22
      • 2016-05-07
      • 2020-02-16
      • 2018-11-22
      • 1970-01-01
      • 2013-05-23
      • 1970-01-01
      相关资源
      最近更新 更多