问题
I am trying to serialize an enum type that is decorated with [Flags] attribute. The enum declaration is as follows:
[Flags]
[ProtoContract(EnumPassthru = true)]
public enum Categories
{
[ProtoEnum(Name = nameof(Invalid), Value = 0x0)]
Invalid = 0x0,
[ProtoEnum(Name = nameof(A), Value = 0x1)]
A = 0x1,
[ProtoEnum(Name = nameof(B), Value = 0x2)]
B = 0x2,
[ProtoEnum(Name = nameof(C), Value = 0x4)]
C = 0x4,
[ProtoEnum(Name = nameof(D), Value = 0x8)]
D = 0x8,
[ProtoEnum(Name = nameof(Global), Value = 0x1 | 0x2 | 0x4 | 0x8)]
Global = A | B | C | D,
}
Now, when I try to serialize the container object, I get
InvalidOperationException:Operation is not valid due to the current state of the object.
Following other similar posts on SO, I have tried the following:
- Add
EnumPassthru = trueparameter in my enum's ProtoContract attribute - Use
RuntimeTypeModel.Default[typeof(Categories)].EnumPassthru = true;at the app startup phase, - Provided the container object's enum-valued field with the IsRequired parameter
[ProtoMember(6, IsRequired = true)]
Is there anything else that I miss with my enum declaration?
The beginning of the exception detail goes like:
InvalidOperationException:Operation is not valid due to the current state of the object.\r\n at ProtoBuf.Serializers.EnumSerializer.EnumToWire(Object value) in c:\Users\onur.gumus\Desktop\protobuf-net-master\protobuf-net\Serializers\EnumSerializer.cs:line 83\r\n at ProtoBuf.Serializers.EnumSerializer.Write(Object value, ProtoWriter dest) in c:\Users\onur.gumus\Desktop\protobuf-net-master\protobuf-net\Serializers\EnumSerializer.cs:line 125\r\n at ProtoBuf.Serializers.FieldDecorator.Write(Object value, ProtoWriter dest) in c:\Users\onur.gumus\Desktop\protobuf-net-master\protobuf-net\Serializers\FieldDecorator.cs:line 38\r\n at ProtoBuf.Serializers.TypeSerializer.Write(Object value, ProtoWriter dest) in c:\Users\onur.gumus\Desktop\protobuf-net-master\protobuf-net\Serializers\TypeSerializer.cs:line 173\r\n at ProtoBuf.Meta.TypeModel.TrySerializeAuxiliaryType(ProtoWriter writer, Type type, DataFormat format, Int32 tag, Object value, Boolean isInsideList) in c:\Users\onur.gumus\Desktop\protobuf-net-master\protobuf-net\Meta\TypeModel.cs:line 125 ...
回答1:
In all easily available (i.e. not ancient) versions of protobuf-net, [Flags] will activate pass-thru behaviour, making this work fine. [ProtoContract(EnumPassThru = true)] will also activate pass-thru behaviour, but is redundant if [Flags] is specified.
In 2.3.0 and above, pass-thru behaviour is also assumed by default as long as you don't have any [ProtoEnum] attributes that actually change the serialized value (which: yours do not) - this is to be more consistent with "proto3", and to make it easier to work with enums in the vast majority of cases.
So: it should not be necessary to do anything here - your code should already just work.
I've tried your code:
- with 2.3.0 and 2.0.0.668
- with the attributes in the question, and with everything except
[Flags]removed - on 2.3.0 with even the
[Flags]attribute removed (although I agree it should be retained in your case - that is definitely a[Flags]enum) - with the enum as the root value, and with the enum as a member marked
[ProtoMember]on an object passed in
In all cases it worked fine. So: in the general case, all I can say is that what you have should already work.
If it is failing in a specific case, it would be great to include a full runnable sample in the question (ideally telling us exactly what framework you are running on), so we can see what you are seeing. This works fine, for example:
using ProtoBuf;
using System;
[Flags]
public enum Categories
{
Invalid = 0x0,
A = 0x1,
B = 0x2,
C = 0x4,
D = 0x8,
Global = A | B | C | D,
}
[ProtoContract]
public class X
{
[ProtoMember(1)]
public Categories Val { get; set; }
public override string ToString() => Val.ToString();
}
static class P
{
static void Main()
{
var orig = new X { Val = Categories.D | Categories.B };
var cloneObj = Serializer.DeepClone(orig);
Console.WriteLine(cloneObj);
var cloneEnum = Serializer.DeepClone(orig.Val);
Console.WriteLine(cloneEnum);
}
}
来源:https://stackoverflow.com/questions/45220365/invalidoperationexception-when-serializing-flags-enum-with-protobuf-net