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

发布于 2024-07-13 16:08:57 字数 719 浏览 9 评论 0原文

在以下示例中:

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);

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

In the following examples:

public class RowData
{
    public object[] Values;
}

public class FieldData
{
    public object Value;
}

I am curious as how either protobuf-net or dotnet-protobufs would handle such classes. I am more familiar with protobuf-net, so what I actually have is:

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

However I get an error saying "No suitable Default Object encoding found". Is there an easy way to treat these classes, that I am just not aware of?

To elaborate more on the use case:

This is a scaled down version of a data class used in remoting. So essentially it looks like this:

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

remoteObject.DoSomething(data);

Note: I've omitted the ISerializable implementation for simplicity, but it is as you'd expect.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧时光的容颜 2024-07-20 16:08:57

关于 protobuf-net,我维护的:

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

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

也许如果您能澄清用例,我也许可以提供更多帮助? 例如,上面的代码对于 DataContractSerializer 或 XmlSerializer 都不太适用...

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

如果您留下更多信息,可以在这里发表评论吗? 所以我可以轻松找到它...或者,直接给我发邮件(请参阅我的个人资料)。


编辑 - 这是我想到的老问题 - 目前不起作用,但明天我会找出原因(可能)。 请注意,理论上额外的成员都可以是私有的 - 我只是想让调试时变得简单。 请注意,这不需要任何额外的存储空间。 就像我说的,它今天不起作用,但它应该 - 我会找出原因......

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

Re protobuf-net, which I maintain:

The issue here isn't value-types (which it will often handle fine) - it is the open object usage, which means it simply doesn't know what data to expect, and thus how to encode/decode it.

At the moment, I can't think of an easy/clean way to handle that. It will handle a range of common value-type scenarios, lists, and any level of hierarchy based on contracts (data-contract, proto-contracts, or some xml-schemas), but it needs a clue.

Perhaps if you can clarify the use-case, I might be able to help more? For example, the above wouldn't work very with DataContractSerializer or XmlSerializer either...

Re dotnet-protobufs; I can't really comment, but I'm pretty sure it would be even less forgiving; it is intended to be used with classes generated from a .proto file, so object would simply never enter into the model (Jon: correct me if I am wrong).

If you do leave more info, could you kindly post a comment here? So I can find it easily... Alternatively, drop me a mail directly (see my SO profile).


edit - here's the hacky thing I had in mind - it isn't working at the moment, but I'll figure out why tomorrow (probably). Note that in theory the extra members could all be private - I'm just trying to make it easy while debugging. Note that this doesn't take any extra storage. Like I say, it doesn't work today, but it should - I'll find out why...

[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
}
灯角 2024-07-20 16:08:57

(更新)

对; 弄清楚了......我上面的示例中的主要问题是价值获取者; 他们抛出异常。 还有一些库故障(现已修复)。

但是,最简单的方法是 Nullable 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;
    }

此方法和*指定方法均已 经过测试,现在工作正常。

(updated)

Right; figured it out... the main problem in my sample above was the value-getters; they were throwing exceptions. There were also some library glitches (now fixed).

However, the simplest approach is Nullable<T> pass-thru properties:

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

etc, with:

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

Both this and the *Specified approach have been tested, and now work fine.

逆流 2024-07-20 16:08:57

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

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

This is something like what I had in mind. Let me know what you think. Naturally I'd have to add a subclass for each value type I need to support. What do you think? Is there a better way, do you see any inefficiencies with this method?

[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)
    { }
}
甜心小果奶 2024-07-20 16:08:57

直接封装似乎工作得很好,所有属性都没有额外的开销,如下所示:

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

Direct encapsulation seems to work fine with no additional overhead from all the properties, in the following manner:

[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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文