Protobuf-net - Как пользоваться oneof

Я быстро поискал информацию об использовании oneofin, и, похоже, он поддерживается начиная с v2.3.0 , но я не могу найти никаких примеров того, как именно его использовать!

Мои требования довольно просты, и, возможно, это также можно решить с помощью [ProtoInclude]но я не совсем уверен, как это будет работать. У меня такой класс:

      [ProtoContract]
public class ProgressUIMessage
{
    [ProtoMember(1)]
    public int Id {get; set;}

    [ProtoMember(2)]
    public object Message {get; set;}
}

Где Messageможет быть 1 из 8 различных известных типов. Типы вообще не наследуются друг от друга, и хотя код можно изменить, у всех типов нет ничего общего.

С использованием Google.ProtobufЯ ожидал сделать что-то , где у меня есть свойство с именем Instrument это может быть один из двух типов в приведенном выше примере, а затем использовать InstrumentOneofCaseчтобы выяснить, какой тип мне дали. Но как мне добиться того же в Protobuf-net?

РЕДАКТИРОВАТЬ: Я оставлю исходный вопрос как есть, но, возможно, лучший вопрос, к которому может относиться больше людей, - это: как бы вы достигли того же, что и в подобноеэтом примере MS в Protobuf-net? Как с точки зрения написания самого класса, так и с точки зрения определения конкретного типа параметра в конечном итоге?

1 ответ

Решение

Чтобы поиграть с этим, нужно взять сообщение из примера MS, который вы цитируете, и пропустить его через protogen, чтобы увидеть, что он делает - что мы можем сделать очень удобно здесь: https://protogen.marcgravell.com/ (примечание I добавляю syntax = "proto3"; в верхней части файла, который опущен в примере MS).

Это дает нам, среди прочего:

          [global::ProtoBuf.ProtoMember(2, Name = @"stock")]
    public Stock Stock
    {
        get => __pbn__instrument.Is(2) ? ((Stock)__pbn__instrument.Object) : default;
        set => __pbn__instrument = new global::ProtoBuf.DiscriminatedUnionObject(2, value);
    }
    public bool ShouldSerializeStock() => __pbn__instrument.Is(2);
    public void ResetStock() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__instrument, 2);

    private global::ProtoBuf.DiscriminatedUnionObject __pbn__instrument;

    [global::ProtoBuf.ProtoMember(3, Name = @"currency")]
    public Currency Currency
    {
        get => __pbn__instrument.Is(3) ? ((Currency)__pbn__instrument.Object) : default;
        set => __pbn__instrument = new global::ProtoBuf.DiscriminatedUnionObject(3, value);
    }
    public bool ShouldSerializeCurrency() => __pbn__instrument.Is(3);
    public void ResetCurrency() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__instrument, 3);

Итак, мы видим, что в основном используется условная сериализация, построенная на основе типа. На самом деле существует множество связанных типов с именами DiscriminatedUnion* - в зависимости от того, что вам нужно пересечь, но поскольку здесь все типы сообщений: DiscriminatedUnionObject у нас работает.


Также есть необязательный параметр «следует использовать перечисление» (как ни странно, «Параметры»), который, если он включен, также добавляет:

          public InstrumentOneofCase InstrumentCase => (InstrumentOneofCase)__pbn__instrument.Discriminator;

    public enum InstrumentOneofCase
    {
        None = 0,
        Stock = 2,
        Currency = 3,
    }

Без этого вам пришлось бы использовать ShouldSerialize*() методы разрешения активного дела.

Надеюсь, это проясняет, как oneof может использоваться с protobuf-net.

Другие вопросы по тегам