Буферы протоколов в C#:Как обрабатываются упакованные типы значений
-
05-09-2019 - |
Вопрос
В следующих примерах:
public class RowData
{
public object[] Values;
}
public class FieldData
{
public object Value;
}
Мне любопытно, как protobuf-net или dotnet-protobufs будут обрабатывать такие классы.Я больше знаком с protobuf-net, поэтому на самом деле у меня есть:
[ProtoContract]
public class RowData
{
[ProtoMember(1)]
public object[] Values;
}
[ProtoContract]
public class FieldData
{
[ProtoMember(1)]
public object Value;
}
Однако я получаю сообщение об ошибке: «Не найдено подходящей кодировки объекта по умолчанию».Есть ли простой способ справиться с этими классами, о котором я просто не знаю?
Чтобы подробнее рассказать о варианте использования:
Это уменьшенная версия класса данных, используемого при удаленном взаимодействии.По сути это выглядит так:
FieldData data = new FieldData();
data.Value = 8;
remoteObject.DoSomething(data);
Примечание:Для простоты я опустил реализацию ISerializable, но все так, как и следовало ожидать.
Решение
(обновлено)
Верно;догадаться...основной проблемой в моем примере выше были методы получения значений;они выбрасывали исключения.Также были некоторые глюки библиотеки (теперь исправлено).
Однако самый простой подход Nullable<T>
сквозные свойства:
[ProtoMember(1)]
private int? ValueInt32
{
get { return Get<int>(); }
set { Value = value; }
}
и т. д., с:
private T? Get<T>() where T : struct
{
return (Value != null && Value is T) ? (T?)Value : (T?)null;
}
И этот, и *указанный подход были проверено, и теперь работает нормально.
Другие советы
Re protobuf-net, который я поддерживаю:
Проблема здесь не в типах значений (с которыми он часто прекрасно справляется), а в открытом доступе. object
Это означает, что он просто не знает, какие данные ожидать и, следовательно, как их кодировать/декодировать.
На данный момент я не могу придумать простой/чистый способ справиться с этим.Он будет обрабатывать ряд распространенных сценариев типов значений, списков и любого уровня иерархии, основанной на контрактах (контракт данных, протоконтракты или некоторые XML-схемы), но для этого требуется зацепка.
Возможно, если вы проясните вариант использования, я смогу помочь больше?Например, приведенное выше не будет работать с DataContractSerializer
или XmlSerializer
или...
Повторно dotnet-protobufs;Я не могу ничего комментировать, но я почти уверен, что это было бы еще менее снисходительно;он предназначен для использования с классами, созданными из файла .proto, поэтому object
просто никогда не войдет в модель (Джон:поправьте меня, если я ошибаюсь).
Если вы оставите дополнительную информацию, не могли бы вы оставить комментарий здесь?Так что я легко найду его...Альтернативно, напишите мне напрямую (см. мой профиль SO).
отредактировать - вот что я имел в виду - в данный момент он не работает, но завтра я выясню, почему (вероятно).Обратите внимание, что теоретически все дополнительные члены могут быть частными — я просто пытаюсь упростить процесс отладки.Обратите внимание, что это не требует дополнительного хранилища.Как я уже сказал, сегодня не работает, а должно - выясню почему...
[ProtoContract]
public class FieldData
{
public object Value {get;set;}
[ProtoMember(1)]
public int ValueInt32 {
get { return (int)Value; } set { Value = value; } }
public bool ValueInt32Specified {
get { return Value != null && Value is int; } set { } }
[ProtoMember(2)]
public float ValueSingle {
get { return (float)Value; } set { Value = value; } }
public bool ValueSingleSpecified {
get { return Value != null && Value is float; } set { } }
// etc for expected types
}
Это что-то вроде того, что я имел в виду.Дайте мне знать, что вы думаете.Естественно, мне пришлось бы добавить подкласс для каждого типа значения, который мне нужно поддерживать.Что вы думаете?Есть ли лучший способ, видите ли вы какие-либо недостатки в этом методе?
[ProtoContract, Serializable]
[ProtoInclude(1, typeof(Int32FieldData))]
public abstract class FieldDataBase : ISerializable
{
[ProtoIgnore]
public abstract object Value { get; set;}
protected FieldDataBase()
{ }
#region ISerializable Members
protected FieldDataBase(SerializationInfo info, StreamingContext context)
{
Serializer.Merge<FieldDataBase>(info, this);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Serializer.Serialize<FieldDataBase>(info, this);
}
#endregion
}
[ProtoContract, Serializable]
public class Int32FieldData : FieldDataBase
{
[ProtoMember(1)]
public int? Int32Value;
[ProtoIgnore]
public override object Value
{
get { return this.Int32Value.HasValue ? this.Int32Value : null; }
set { this.Int32Value = (int?)value; }
}
public Int32FieldData() { }
protected Int32FieldData(SerializationInfo info, StreamingContext context)
:base(info, context)
{ }
}
Прямая инкапсуляция, похоже, работает нормально без каких-либо дополнительных затрат со стороны всех свойств следующим образом:
[ProtoContract, Serializable]
public class ObjectWrapper : ISerializable
{
public ObjectWrapper()
{ }
[ProtoIgnore]
public object Value
{
get
{
if (Int32Value.HasValue)
return Int32Value.Value;
else if (BinaryValue != null)
return BinaryValue;
else
return StringValue;
}
set
{
if (value is int)
this.Int32Value = (int)value;
else if (value is byte[])
this.BinaryValue = (byte[])value;
else if (value is string)
this.StringValue = (string)value;
}
}
[ProtoMember(1)]
private int? Int32Value;
[ProtoMember(2)]
private string StringValue;
[ProtoMember(3)]
private byte[] BinaryValue;
// etc
#region ISerializable Members
protected ObjectWrapper(SerializationInfo info, StreamingContext context)
{
Serializer.Merge<ObjectWrapper>(info, this);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Serializer.Serialize<ObjectWrapper>(info, this);
}
#endregion
}