Следуя из этого вопроса, какой был бы лучший способ представить объект System.Decimal в буфере протокола?
Какой лучший способ представить System.Decimal в буферах протоколов?
Ответ 1
Ну, protobuf-net просто справится с этим для вас; он исчерпывает свойства типов и имеет полную поддержку decimal
. Поскольку нет прямого способа выражения decimal
в proto, он не будет (в настоящее время) генерировать свойство decimal
из файла .proto, но было бы неплохо настроить какой-то общий тип ( "BCL.Decimal" или аналогичный) и интерпретировать его как десятичное.
Что касается его представления - у меня был дискуссионный документ об этом (теперь устаревший, я подозреваю) в области виджета protobuf-net; теперь есть рабочая версия в protobuf-net, которая просто делает это для вас.
Без сомнения, мы с Джоном заберут это позже позже;-p
Версия protobuf-net этого (в .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
}
Ответ 2
У нас с Марком очень расплывчатые планы придумать библиотеку "общего PB-сообщения", так что вы можете представлять довольно распространенные типы (дату/время и десятичную пружину мгновенно на ум) обычным способом, с доступными преобразованиями .NET и Java (и все остальное, кто хочет внести свой вклад).
Если вы счастливы придерживаться .NET, и вы ищете компактность, я бы мог пойти с чем-то вроде:
message Decimal {
// 96-bit mantissa broken into two chunks
optional uint64 mantissa_msb = 1;
optional uint32 mantissa_lsb = 2;
required sint32 exponent_and_sign = 3;
}
Знак может быть просто представлен знаком показателя exponent_and_sign, причем показателем является абсолютное значение.
Предоставление обеим частям мантиссы необязательно означает, что 0 представляется очень компактно (но все же дифференцируется между 0m и 0.0000m и т.д.). exponent_and_sign также может быть необязательным, если мы действительно хотели.
Я не знаю о проекте Marc, но в моем порт я генерирую частичные классы, поэтому вы можете поместить преобразование между System.Decimal и Protobuf.Common.Decimal(или что-то еще) в частичный класс.
Ответ 3
Несколько проще реализовать подход, чем Jon или Marc, - сохранить его как 4 значения sint32
, что удобно тривиально сопоставить с выводом Decimal.GetBits().
Файл прото будет выглядеть так:
message ProtoDecimal {
sint32 v1 = 1;
sint32 v2 = 2;
sint32 v3 = 3;
sint32 v4 = 4;
}
И конвертер будет:
public decimal ConvertToDecimal(AbideDecimal value)
{
return new decimal(new int[] { value.V1, value.V2, value.V3, value.V4 });
}
public ProtoDecimal ConvertFromDecimal(decimal value)
{
var bits = decimal.GetBits(value);
return new ProtoDecimal
{
V1 = bits[0],
V2 = bits[1],
V3 = bits[2],
V4 = bits[3]
}
}
Это может быть не так просто в других языках, но если вам нужно только нацелиться на С#, тогда он займет столько же максимум 16 байт, как и другой подход (хотя значения, подобные 0, могут быть не так компактно сохранены - я не знаю достаточно о сложных деталях того, как Protobuf хранит целые), будучи гораздо понятнее тупым программистам вроде меня :)
Очевидно, вам придется гонять на лошадях, если вы хотите проверить работоспособность, но я сомневаюсь, что в этом есть что-то особенное.
Ответ 4
Я собрал патч для protobuf-csharp-port с помощью hooks, который генерирует классы protobuf с собственными структурами Decimal и DateTime. Проводной формат мудрый, они представлены двумя "встроенными" прото-сообщениями.
Ответ 5
Когда вы знаете, что у вас есть ограниченное количество десятичных знаков, вы можете использовать наименьшую возможную единицу в качестве целочисленного значения. Например, при обработке денег не требуется десятичный тип, а вместо этого можно определить использование центовых единиц. Тогда целое число со значением 2
будет ссылаться на 0.02
в любой используемой валюте.