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 のサブタイプとして boolean を追加することはできません。

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 に答える