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

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

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

    在以下示例中:

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

    我很好奇protobuf-net或dotnet protobuf如何处理这些类。我更熟悉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 回复  |  直到 14 年前
        1
  •  3
  •   Marc Gravell    16 年前

    (更新)

    是的,知道了……上面我的示例中的主要问题是值getter;它们抛出了异常。还有一些图书馆的小故障( 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 现在工作得很好。

        2
  •  6
  •   Marc Gravell    16 年前

    回复Protobuf网,我认为:

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

    目前,我想不出一个简单/干净的方法来处理这个问题。它将处理一系列通用的值类型方案、列表以及基于契约的任何层次结构(数据契约、原型契约或一些XML模式),但它需要 线索 .

    如果你能澄清用例,我也许能提供更多帮助?例如,上面的内容不适用于 DataContractSerializer XmlSerializer 要么…

    re-dotnet protobufs;我不能发表评论,但我很确定这会更不容易理解;它是用于从.proto文件生成的类,所以 对象 根本不会进入模型(乔恩:如果我错了,请纠正我)。

    如果你留下更多的信息,你能在这里发表评论吗?所以我很容易找到…或者,直接给我一封邮件(参见我的销售代表简介)。


    编辑-这是我想的黑客的事情-它目前不工作,但我明天(可能)会找出原因。请注意,理论上,额外的成员可能都是私有的——我只是想在调试时让它变得容易些。请注意,这不会占用任何额外的存储空间。就像我说的,它今天不起作用,但它应该——我会找出为什么……

    [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
    }
    
        3
  •  0
  •   Greg Dean    16 年前

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

    [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    16 年前

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

    [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
    }