5

シリアライズしたい場合は、protobuf-net を使用して 10 進数をデシリアライズします。

const decimal originalDecimal = 1.6641007661819458m;
using (var memoryStream = new MemoryStream())
{
    Serializer.Serialize(memoryStream, originalDecimal);
    memoryStream.Position = 0;
    var deserializedDecimal = Serializer.Deserialize<decimal>(memoryStream);
    Assert.AreEqual(originalDecimal, deserializedDecimal);
}

それは正常に動作します。Protobuf-net は内部的に以下の小数表現を使用します (cf. Bcl.proto ):

message Decimal {
  optional uint64 lo = 1; // the first 64 bits of the underlying value
  optional uint32 hi = 2; // the last 32 bis of the underlying value
  optional sint32 signScale = 3; // the number of decimal digits, and the sign
}

ここで、同等と思われるプロト コントラクトをコードで定義するとします。

[ProtoContract]
public class MyDecimal
{
    [ProtoMember(1, IsRequired = false)]
    public ulong Lo;

    [ProtoMember(2, IsRequired = false)]
    public uint Hi;

    [ProtoMember(3, IsRequired = false)]
    public int SignScale;
}

...その後、aをシリアライズしてバックを取得することも、a をシリアライズしてバックdecimalを取得することもできません。MyDecimalMyDecimaldecimal

から: decimal_MyDecimal

const decimal originalDecimal = 1.6641007661819458m;
using (var memoryStream = new MemoryStream())
{
    Serializer.Serialize(memoryStream, originalDecimal);
    memoryStream.Position = 0;

    // following line throws a Invalid wire-type ProtoException
    Serializer.Deserialize<MyDecimal>(memoryStream);
}

から: MyDecimal_decimal

var myDecimal = new MyDecimal
{
    Lo = 0x003b1ee886632642,
    Hi = 0x00000000,
    SignScale = 0x00000020,
};

using (var memoryStream = new MemoryStream())
{
    Serializer.Serialize(memoryStream, myDecimal);
    memoryStream.Position = 0;

    // following line throws a Invalid wire-type ProtoException
    Serializer.Deserialize<decimal>(memoryStream);
}

ここで何か不足していますか?

私は、プロトコル バッファーを介して C# アプリケーションと通信する必要がある C++ アプリケーションに取り組んでおり、10 進数の逆シリアル化が失敗する理由を理解できません。

4

1 に答える 1

3

これは、「それはオブジェクトですか?それとも裸の値ですか?」のエッジ ケースです。たとえば、protobuf で単にシリアル化することはできません。ラッパー オブジェクトが必要です。intしたがって、ネイキッド値の場合、その値が実際には架空のラッパー オブジェクトのフィールド 1 であるかのように見せかけます。ただし、 の場合decimal、これは少し注意が必要です。これは、decimalが実際にはオブジェクトであるかのようにエンコードされているためです。技術的には裸の値として書く decimal ことができます...しかし、そうではないようですそれをラップしています)-そして、この段階でそれを修正することは良い考えではないでしょうか.

基本的に、ネイキッド値をシリアル化する代わりに、値を持つオブジェクトをシリアル化すると、これはより確実に機能します。またより効率的に機能します (protobuf-net は、既知の型を探します。裸の値は非常にフォールバック シナリオです)。例えば:

[ProtoContract]
class DecimalWrapper {
    [ProtoMember(1)]
    public decimal Value { get; set; }
}
[ProtoContract]
class MyDecimalWrapper {
    [ProtoMember(1)]
    public MyDecimal Value { get; set; }
}

これらをシリアル化すると、100% 互換性があります。

const decimal originalDecimal = 1.6641007661819458m;
using (var memoryStream = new MemoryStream())
{
    var obj = new DecimalWrapper { Value = originalDecimal };
    Serializer.Serialize(memoryStream, obj);
    // or, as it happens (see text) - this is equal to
    // Serializer.Serialize(memoryStream, originalDecimal);

    memoryStream.Position = 0;
    var obj2 = Serializer.Deserialize<MyDecimalWrapper>(memoryStream);
    Console.WriteLine("{0}, {1}, {2}",
        obj2.Value.Lo, obj2.Value.Hi, obj2.Value.SignScale);
    // ^^^ 16641007661819458, 0, 32

    memoryStream.SetLength(0);
    Serializer.Serialize(memoryStream, obj2);
    memoryStream.Position = 0;
    var obj3 = Serializer.Deserialize<DecimalWrapper>(memoryStream);

    bool eq = obj3.Value == obj.Value; // True
}

実際には、protobuf-netはオブジェクトが存在するふりSerialize<decimal>をするため、 100% 互換性があると言うのも事実ですが、あなた自身の正気を保つためには、単純な「常に DTO インスタンスをシリアル化する」アプローチに固執する方Serialize<MyDecimalWrapper>がおそらく簡単です。 「これは DTO ですか?それとも裸の値ですか?」と考える必要はありません。


最終的な考えとして: 相互運用機能を使用している場合はdecimal、protobuf 仕様で定義されていないため、避けることをお勧めします。プラットフォームが異なると、「10 進数」型の意味が異なることがよくあります。protobuf-netは、主に protobuf-net がより広い範囲の DTO を (それ自体に) ラウンドトリップできるようにする意味を発明しますが、その値を任意のプラットフォームに解析するのは厄介な場合がありますクロスプラットフォームで を使用する場合decimalは、/ 、または/を介した固定精度または.doublefloatlongulongstring

于 2013-05-15T12:26:22.493 に答える