Буферы протокола в C #: как обрабатываются типы значений в штучной упаковке - PullRequest
4 голосов
/ 18 февраля 2009

В следующих примерах:

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 для простоты, но это, как и следовало ожидать.

Ответы [ 4 ]

6 голосов
/ 19 февраля 2009

Re protobuf-net, который я поддерживаю:

Проблема здесь не в типах значений (с которыми она часто справляется) - это использование object, что означает, что она просто не знает, какие данные ожидать, и, следовательно, как кодировать / декодировать это.

В настоящее время я не могу придумать простой / чистый способ справиться с этим. Он будет обрабатывать ряд общих сценариев типа значений, списков и любого уровня иерархии на основе контрактов (контракт на данные, протоконтракты или некоторые xml-схемы), но для этого требуется ключ .

Возможно, если вы сможете уточнить пример использования, я мог бы помочь больше? Например, приведенное выше не очень хорошо работает с DataContractSerializer или XmlSerializer либо ...

Re dotnet-protobufs; Я не могу комментировать, но я уверен, что это будет еще менее прощающим; он предназначен для использования с классами, сгенерированными из файла .proto, поэтому object просто никогда не войдет в модель (Джон: поправьте меня, если я ошибаюсь).

Если вы оставите больше информации, не могли бы вы оставить комментарий здесь? Так что я могу легко его найти ... Либо напишите мне письмо напрямую (см. Мой профиль SO).


edit - вот хакерская вещь, которую я имел в виду - в данный момент она не работает, но я выясню, почему завтра (возможно). Обратите внимание, что теоретически все дополнительные члены могут быть приватными - я просто пытаюсь упростить процесс отладки. Обратите внимание, что это не займет никакого дополнительного места. Как я уже сказал, сегодня это не работает, но должно - я пойму, почему ...

[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
}
3 голосов
/ 19 февраля 2009

(обновлено)

справа; понял это ... главная проблема в моем примере выше - получатели стоимости; они бросали исключения. Были также некоторые глюки библиотеки ( теперь исправлено ).

Однако самый простой подход - Nullable<T> pass-thru properties:

    [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;
    }

И этот, и * указанный подход были проверены и теперь работают нормально.

0 голосов
/ 19 февраля 2009

Кажется, что прямая инкапсуляция работает без лишних затрат на все свойства следующим образом:

[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
}
0 голосов
/ 19 февраля 2009

Это что-то вроде того, что я имел в виду. Дайте мне знать, что вы думаете. Естественно, я должен был бы добавить подкласс для каждого типа значения, которое мне нужно поддерживать. Как вы думаете? Есть ли лучший способ, вы видите какие-либо недостатки этого метода?

[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)
    { }
}
...