代码之家  ›  专栏  ›  技术社区  ›  Greg Dean

C#中的协议缓冲区:如何处理盒装值类型

  •  4
  • Greg Dean  · 技术社区  · 17 年前

    在以下示例中:

    public class RowData
    {
        public object[] Values;
    }
    
    public class FieldData
    {
        public object Value;
    }
    

    我很好奇protobuf-net或dotnet-probufs将如何处理这样的类。我更熟悉protobuf网,所以我实际拥有的是:

    [ProtoContract]
    public class RowData
    {
        [ProtoMember(1)]
        public object[] Values;
    }
    [ProtoContract]
    public class FieldData
    {
        [ProtoMember(1)]
        public object Value;
    }
    

    要详细说明用例:

    FieldData data = new FieldData();
    data.Value = 8;
    
    remoteObject.DoSomething(data);
    

    注意:为了简单起见,我省略了ISerializable实现,但正如您所期望的那样。

    4 回复  |  直到 16 年前
        1
  •  3
  •   Marc Gravell    17 年前

    关于protobuf网,我维护:

    这里的问题不是值类型(它通常会很好地处理),而是开放的 object 使用,这意味着它根本不知道会有什么数据,因此也不知道如何对其进行编码/解码。

    目前,我想不出一种简单/干净的方法来处理这个问题。它将处理一系列常见的值类型场景、列表和基于契约的任何层次结构(数据契约、原型契约或一些xml模式),但它需要一个 .

    DataContractSerializer XmlSerializer

    Re-dotnet-protobufs;我真的不能评论,但我很确定这会更不宽容;它旨在与从.proto文件生成的类一起使用,因此 对象 根本不会进入模型(乔恩:如果我错了,请纠正我)。

    如果你确实留下了更多信息,你能在这里发表评论吗?所以我可以很容易地找到它。..或者,直接给我发邮件(见我的SO个人资料)。


    编辑——这是我想到的一件棘手的事情——它目前不起作用,但我明天(可能)会弄清楚原因。请注意,理论上,额外的成员都可以是私有的——我只是想在调试时让它更容易。请注意,这不会占用任何额外的存储空间。就像我说的,它今天不起作用,但它应该——我会找出原因的。..

    [ProtoContract]
    public class FieldData
    {
        public object Value {get;set;}
    
        [ProtoMember(1)]
        public int ValueInt32 {
            get { return (int)Value; } set { Value = value; } }
        public bool ValueInt32Specified {
            get { return Value != null && Value is int; } set { } }
    
        [ProtoMember(2)]
        public float ValueSingle {
            get { return (float)Value; } set { Value = value; } }
        public bool ValueSingleSpecified {
            get { return Value != null && Value is float; } set { } }
    
        // etc for expected types
    }
    
        2
  •  6
  •   Marc Gravell    17 年前

    now fixed ).

    然而,最简单的方法是 Nullable<T> 传递属性:

        [ProtoMember(1)]
        private int? ValueInt32
        {
            get { return Get<int>(); }
            set { Value = value; }
        }
    

    等等,包括:

        private T? Get<T>() where T : struct
        {
            return (Value != null && Value is T) ? (T?)Value : (T?)null;
        }
    

    这和*指定的方法都是 tested

        3
  •  0
  •   Greg Dean    17 年前

    这和我想的差不多。让我知道你的想法。当然,我必须为我需要支持的每种值类型添加一个子类。你怎么认为?有没有更好的方法,你看到这种方法有什么低效之处吗?

    [ProtoContract, Serializable]
    [ProtoInclude(1, typeof(Int32FieldData))]
    public abstract class FieldDataBase : ISerializable
    {
        [ProtoIgnore]
        public abstract object Value { get; set;}
        protected FieldDataBase()
        { }
    
        #region ISerializable Members
        protected FieldDataBase(SerializationInfo info, StreamingContext context)
        {
            Serializer.Merge<FieldDataBase>(info, this);
        }
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Serializer.Serialize<FieldDataBase>(info, this);
        }
    
        #endregion
    }
    
    [ProtoContract, Serializable]
    public class Int32FieldData : FieldDataBase
    {
        [ProtoMember(1)]
        public int? Int32Value;
    
        [ProtoIgnore]
        public override object Value
        {
            get { return this.Int32Value.HasValue ? this.Int32Value : null; }
            set { this.Int32Value = (int?)value; }
        }
        public Int32FieldData() { }
        protected Int32FieldData(SerializationInfo info, StreamingContext context)
            :base(info, context)
        { }
    }
    
        4
  •  0
  •   Greg Dean    17 年前

    直接封装似乎可以很好地工作,所有属性都没有额外的开销,方式如下:

    [ProtoContract, Serializable]
    public class ObjectWrapper : ISerializable
    {
        public ObjectWrapper()
        { }
        [ProtoIgnore]
        public object Value
        {
            get
            {
                if (Int32Value.HasValue)
                    return Int32Value.Value;
                else if (BinaryValue != null)
                    return BinaryValue;
                else
                    return StringValue;
            }
            set
            {
                if (value is int)
                    this.Int32Value = (int)value;
                else if (value is byte[])
                    this.BinaryValue = (byte[])value;
                else if (value is string)
                    this.StringValue = (string)value;
            }
        }
        [ProtoMember(1)]
        private int? Int32Value;
        [ProtoMember(2)]
        private string StringValue;
        [ProtoMember(3)]
        private byte[] BinaryValue;
                // etc
    
        #region ISerializable Members
        protected ObjectWrapper(SerializationInfo info, StreamingContext context)
        {
            Serializer.Merge<ObjectWrapper>(info, this);
        }
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Serializer.Serialize<ObjectWrapper>(info, this);
        }
    
        #endregion
    }