1

我有一堂课:

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

但我不能使用属性。

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

4

1 回答 1

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
    }
}
于 2012-09-26T12:10:37.430 回答