How are int ProtoMember / ProtoInclude separated / placed in ProtoBuf?
I have a couple of questions about how / where the identifier should be declared [ProtoContract]
.
Imagine the following code:
[ProtoContract]
[ProtoInclude(100, typeof(SomeClassA))]//1) CAN I USE 1 here?
public abstract class RootClass{
[ProtoMember(1)]
public int NodeId {get;set;}
}
[ProtoContract]
[ProtoInclude(200, typeof(SomeClassC)]//2) Should I declare this here or directly on the RootClass?
//3) Can I use the id 100 here?
//4) Can I use the id 1 here? or member + include share the id?
public class SomeClassA : RootClass{
[ProtoMember(1)]//5) CAN I USE 1 here? Since the parent already use it but it a different class
public String Name{get;set;}
}
[ProtoContract]
public class SomeClassC : SomeClassA {
[ProtoMember(2)]
public int Count{get;set;}
}
[ProtoContract]
public class SomeClassD : SomeClassA {
[ProtoMember(2)] //6) Can I use 2 here? Since SomeClassC already use it and is a sibling?
public int Count{get;set;}
}
I have posed several questions with questions:
- CAN I USE 1 here?
- Should I declare this here or directly on the RootClass?
- Can I use ID 100 here?
- Can I use ID 1 here? or member + enable id sharing?
- CAN I USE 1 here? Since the parent is already using it, but it is different from the class
- Can I use 2 here? Since SomeClassC is already using it and is a sibling?
The thing is, we have a huge model with a lot of classes that all inherit from the same object, so I'm trying to figure out which ID I should take care of.
source to share
Short version:
- the set of field numbers for a type is union numbers defined against members (fields and properties), and numbers defined for immediate subtypes (includes)
- the set of field numbers must be unique within , for one type - no need to consider basic types or derived types
Longer version:
The reason for this is that subtypes essentially appear as optional fields:
[ProtoContract]
[ProtoInclude(100, typeof(SomeClassA))]
public abstract class RootClass{
[ProtoMember(1)]
public int NodeId {get;set;}
}
[ProtoContract]
[ProtoInclude(200, typeof(SomeClassC)]
public class SomeClassA : RootClass{
[ProtoMember(1)]
public String Name{get;set;}
}
[ProtoContract]
public class SomeClassC : SomeClassA {
[ProtoMember(2)]
public int Count{get;set;}
}
has the syntax proto2
:
message RootClass {
optional int32 NodeId = 1;
optional SomeClassA _notNamed = 100;
}
message SomeClassA {
optional string Name = 1;
optional SomeClassC _notNamed = 200;
}
message SomeClassC {
optional int32 Count = 2;
}
Note that at most 1 subtype field will be used, so it can be read oneof
for purposes .proto
. Any fields related to the subtype will be included in message SomeClassA
, so there is no conflict with RootClass
, and they do not have to be unique. The numbers should be unique only for message
the sense .proto
.
To ask specific questions, follow these steps:
- no, because it would contradict
NodeId
- it must be declared on
SomeClassA
; protobuf-net only expects immediate descendants and keeps the numbering consistent and easy to read, since the field number is only required to be consistent with membersSomeClassA
- Yes, you can; no conflict
- no, because it would contradict
Name
- Yes, you can; no conflict
-
Yes, you can; there is no conflict, but in fact protobuf-net does not even think of
SomeClassD
as a sibling (he never advertised as include), but ifSomeClassA
was[ProtoInclude(201, typeof(SomeClassD))]
, then it would be good. This will modify ours.proto
to add:optional SomeClassD _alsoNotNamed = 201;
before
message SomeClassA
and add:message SomeClassD { optional int32 Count = 2; }
Note that protobuf-net does not actually generate syntax .proto
unless you explicitly ask for it (via GetSchema<T>
, etc.). I am including it solely for illustrative purposes in terms of basic protobuf concepts.
source to share