Как правильно реализовать полиморфизм с protobuf. * ​​1000 * и рефлексией - PullRequest
3 голосов
/ 03 апреля 2020

Предположим, у меня есть иерархия классов, реализованная в c# с Protobuf.net. (Также был бы класс Rectangle, который реализует Shape, но для краткости я его опустил.)

[ProtoContract]
[ProtoInclude(1, typeof(Circle))]
public class Shape {
}

[ProtoContract]
public class Circle : Shape {

    [ProtoMember(1)]
    public int Radius {get;set;}
}

Я хочу представить эту же иерархию классов в TypeScript, используя protobuf.js. Однако я не могу понять, как сопоставить поле oneof с подклассом.

Это лучшее, что я смог придумать:

// It's not possible to make this inherit from 'Shape'
class Circle extends Message<Circle> {
  @Field.d(1, "int32")
  radius: number;
}

class Shape extends Message<Shape> implements IShape {
  @Field.d(1, Circle)
  circle: Circle;

  @OneOf.d("circle") // ,"rectangle"
  which: string;

  // Implements the property on ICircle
  get radius(): number {
    return this.circle.radius;
  }
}

interface IShape {
}

interface ICircle extends IShape {
  radius: number;
}

function isCircle(shape: IShape): shape is ICircle {
  return (shape as any).which === "circle";
}

const shape = new Shape({
  circle: new Circle({ radius: 5 }),
  which: "circle"
});

const buffer = Shape.encode(shape).finish();
const decoded = Shape.decode(buffer);

if (isCircle(decoded)) {
  const itsACircle: ICircle = decoded;
  // Do something
}

Мне это кажется счастливым, потому что:

  1. Мы должны выставить все возможные поля подкласса как свойства базового Shape класса (в данном случае radius свойство Circle).
  2. Ни один из классов на самом деле не реализует ICircle, поэтому, если мы не сможем должным образом раскрыть некоторые свойства, компилятор не предупредит нас.
  3. Защита типов использует приведение к any.

Кто-нибудь может предложить лучший способ достижения этого?

...