使用protobuf-net序列化IConvertible字段

时间:2012-09-26 11:42:30

标签: c# protobuf-net

我有一个班级:

internal class Value
{
    internal IConvertible Min { get; set; }
}

和测试用例:

[TestMethod]
public void Custom_IConvertible()
{
    RuntimeTypeModel.Default.
        Add(typeof(Value), false).
        Add(1, "Min");

    RuntimeTypeModel.Default.
        Add(typeof(IConvertible), false);

    var val = new Value {Min = true};
    var result = Serializer.DeepClone(val);
    Assert.AreEqual(true, result.Min);
}

抛出:

System.InvalidOperationException: Unexpected sub-type: System.Boolean

但是我不能添加布尔值作为IConvertible的子类型,因为我会得到:

System.ArgumentException: Data of this type has inbuilt behaviour, and 
cannot be added to a model in this way: System.Boolean 

正如我所看到的,IConvertible只能通过BCL基类型实现,这种情况应该正常工作。我在这里缺少什么?


受到Marc在下面的回应的启发,我改写了我的样本。

上课:

internal abstract class Value
{
    internal IConvertible Min { get; set; }

    internal static Value Create<T>(T min) where T :IConvertible
    {
        return new ValueT<T> {Min = min};
    }

    internal sealed class ValueT<T> : Value where T : IConvertible
    {
        internal new T Min
        {
            get { return (T) base.Min; }
            set { base.Min = value; }
        }
    }
}

和测试用例:

[TestMethod]
public void Custom_IConvertible()
{
    RuntimeTypeModel.Default.
        Add(typeof(Value), false).
        AddSubType(10, typeof(Value.ValueT<bool>));

    RuntimeTypeModel.Default.
        Add(typeof(Value.ValueT<bool>), false).
        Add(1, "Min");

    var val = Value.Create(true);
    var result = Serializer.DeepClone(val);
    Assert.AreEqual(true, result.Min);
}

调用Add(1,“Min”)时出现异常:

System.ArgumentException: Unable to determine member: Min
Parameter name: memberName

但是,如果我将类定义为(相同的代码,但具有属性),它将正常工作:

[ProtoContract]
[ProtoInclude(1, typeof(ValueT<int>))]
internal abstract class Value
{
    internal IConvertible Min { get; set; }

    internal static Value Create<T>(T min) where T :IConvertible
    {
        return new ValueT<T> {Min = min};
    }

    [ProtoContract]
    internal sealed class ValueT<T> : Value where T : IConvertible
    {
        [ProtoMember(1)]
        internal new T Min
        {
            get { return (T) base.Min; }
            set { base.Min = value; }
        }
    }
}

[TestMethod]
public void Custom_IConvertible()
{
    var val = Value.Create(true);
    var result = Serializer.DeepClone(val);
    Assert.AreEqual(true, result.Min);
}

但我不能使用属性。

这是一个错误还是我错过了一些明显的东西?

1 个答案:

答案 0 :(得分:2)

那不行。 protobuf-net对数据契约(基本上)起作用,IConvertible 一个契约 - 运行时的实际值可以是任何东西。 protobuf-net想提前知道 它是什么。

支持protobuf-net中的接口,但这适用于不同的场景。

它们是BCL基元的事实是无关紧要的;出于完全相同的原因,internal object Min {get;set;}会失败。

tl; dr:这不是支持的方案。


这是可以工作的重新工作(请注意,为方便起见,我使用了属性进行配置,但它也可以通过运行时配置工作):

using ProtoBuf;
using System;
[ProtoContract]
[ProtoInclude(1, typeof(ValueImpl<int>))]
[ProtoInclude(2, typeof(ValueImpl<float>))]
[ProtoInclude(3, typeof(ValueImpl<double>))]
// other types you want to support
internal abstract class Value
{
    public static Value Create<T>(T value) where T : IConvertible
    {
        return new ValueImpl<T> { Min = value };
    }
    public IConvertible Min { get { return MinImpl;} set { MinImpl = value;}}
    protected abstract IConvertible MinImpl {get;set;}
    [ProtoContract]
    public sealed class ValueImpl<T> : Value where T : IConvertible
    {
        [ProtoMember(1)]
        public new T Min { get; set; }
        protected override IConvertible MinImpl
        {
            get { return Min; }
            set { Min = (T)value; }
        }
    }
}

static class Program
{
    static void Main()
    {       
        var val = Value.Create(true);
        var result = Serializer.DeepClone(val);

        bool x = (bool)val.Min; // true
    }
}