【问题标题】:Protocol Buffers In C#: How Are Boxed Value Types HandledC# 中的协议缓冲区:如何处理盒装值类型
【发布时间】:2009-02-18 19:55:36
【问题描述】:

在以下示例中:

public class RowData
{
    public object[] Values;
}

public class FieldData
{
    public object Value;
}

我很好奇 protobuf-net 或 dotnet-protobufs 将如何处理这些类。我对protobuf-net比较熟悉,所以我实际拥有的是:

[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 实现,但正如您所期望的那样。

【问题讨论】:

  • OK - 有一种方法可以做到; t'is hacky,但它应该可以工作......在路上更新......

标签: .net protocol-buffers protobuf-net


【解决方案1】:

关于我维护的 protobuf-net:

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

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

也许如果你能澄清用例,我也许能提供更多帮助?例如,上述内容不适用于DataContractSerializerXmlSerializer...

重新 dotnet-protobufs;我无法真正发表评论,但我敢肯定它会更不宽容;它旨在与从 .proto 文件生成的类一起使用,因此 object 永远不会进入模型(Jon:如果我错了,请纠正我)。

如果您确实留下了更多信息,您可以在这里发表评论吗?所以我可以很容易地找到它......或者,直接给我发邮件(请参阅我的 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
}

【讨论】:

  • DataContractSerializer我无法评论,但是XmlSerializer对此没有问题。
  • 我更多地考虑继承。我会发布我的想法。
  • @PatrickSzalapski 你有什么不开心的特定场景吗?
  • 只是想知道“你心目中的骇人听闻的事情”是否可以或应该起作用。
  • @Patrick 动态类型支持提供了一些这样的功能,但它并不适合所有场景
【解决方案2】:

(更新)

对;想通了...上面示例中的主要问题是价值获取者;他们正在抛出异常。还有一些库故障 (now fixed)。

不过,最简单的方法是Nullable<T> pass-thru 属性:

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

这个方法和 *Specified 方法都是 tested,现在可以正常工作了。

【讨论】:

    【解决方案3】:

    这和我的想法一样。让我知道你的想法。自然,我必须为我需要支持的每种值类型添加一个子类。你怎么看?有没有更好的方法,您是否发现这种方法效率低下?

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

    【讨论】:

    • 嗯,好像空值有问题?
    • 以何种方式“出问题”?不幸的是,有线格式没有空值的概念,所以我们能做的最接近的就是不传输它——但无论哪种方式,继承都应该起作用。你有一个具体的问题吗? (我现在没时间尝试...)
    • 关于信息 - 由于编码细节,直接封装比继承更有效(每个值 2-4 个字节)。
    • 好吧-正如您所说,问题是没有传输空值。我正在努力解决问题,但它变得很糟糕^2 :-)
    • 我想我应该指出,我已经转到 int?[] 在这种情况下我最终序列化 {1,2,3,4,null} 并得到 {1,2,3 ,4} 后退
    【解决方案4】:

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

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

    【讨论】:

    • string 和 byte[] 在一般意义上并不是真正需要的 - 我只知道在我的特定情况下我会需要它们
    猜你喜欢
    • 1970-01-01
    • 2021-12-19
    • 2012-01-21
    • 2011-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-06
    相关资源
    最近更新 更多