我有一个班级:
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);
}
但我不能使用属性。
这是一个错误还是我错过了一些明显的东西?
答案 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
}
}