Скажем, я хочу сериализовать, а затем десериализовать десятичную с использованием protobuf-net:
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 внутренне использует следующее представление для десятичных дробей (ср. 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;
}
…тогда я не могу сериализовать decimal
и получить MyDecimal
обратно, ни сериализовать MyDecimal
и получить decimal
назад.
От 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 # через буферы протокола и не могу понять, почему десятичная десериализация не удалась.
Это крайний случай «это объект? Или голое значение?». Ты не можешь просто сериализовать int
Скажем, в protobuf — вам нужен объект-обертка. Следовательно, для обнаженных значений он делает вид, что значение фактически является полем 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% совместим с Serialize<MyDecimalWrapper>
, но для вашего же здравомыслия это, вероятно, просто Полегче придерживаться простого подхода «всегда сериализировать экземпляр DTO», вместо того, чтобы думать «это DTO?
В заключение: если вы используете взаимодействие, я бы предложил избегать decimal
, поскольку это не определено в спецификации protobuf, и разные платформы часто имеют разное значение своего «десятичного» типа. Protobuf-сеть изобретает смысл, в основном, в том, чтобы позволить protobuf-net совершать обходные пути (к себе) в более широком диапазоне DTO, но может быть неудобно анализировать это значение в произвольной платформе. При работе с кроссплатформенностью и использовании decimal
, Рекомендую принимая во внимание вещи как double
/float
или некоторая фиксированная точность с помощью long
/ulong
или, может быть, даже просто string
,
Других решений пока нет …