C# 在Proto-Buf中使用bool类型
我在我的应用程序中使用protobuf net进行序列化/反序列化。我面临一个问题C# 在Proto-Buf中使用bool类型,c#,protobuf-net,C#,Protobuf Net,我在我的应用程序中使用protobuf net进行序列化/反序列化。我面临一个问题 [ProtoContract()] ClsTest { private bool _isPeriodic [ProtoMember(1)] public bool IsPeriodic { get { return _isPeriodic; } set {
[ProtoContract()]
ClsTest
{
private bool _isPeriodic
[ProtoMember(1)]
public bool IsPeriodic
{
get
{
return _isPeriodic;
}
set
{
isPeriodic = value;
}
}
}
我在collection对象中使用这个类
序列化过程工作正常,但在反序列化之后,属性IsPeriodic的值默认为true,尽管在某些情况下为false。有人能帮我吗?以下对我来说很好:
[ProtoContract]
class ClsTest
{
[ProtoMember(1)]
public bool IsPeriodic { get; set; }
}
反序列化:
// stream is a NetworkStream object
ClsTest clsTestObj = Serializer.DeserializeWithLengthPrefix<ClsTest>(stream, PrefixStyle.Fixed32);
bool value = clsTestObj.IsPeriodic;
//流是NetworkStream对象
ClsTest clsTestObj=序列化程序.DeserializeWithLengthPrefix(stream,PrefixStyle.Fixed32);
bool值=clsTestObj.IsPeriodic;
我猜您的代码正在将默认新
实例的IsPeriodic
设置为true
,可能:
private bool _isPeriodic = true;
或者,在构造函数中,您有:
_isPeriodic = true; // or IsPeriodic = true
基本上,存在一个隐式默认值(遵循protobuf语言指南),其中bool假定默认值为false
。它不发送被认为是默认的数据。如果此默认值不正确,请告诉它:
[ProtoMember(1), DefaultValue(true)]
或者IIRC您可以尝试将IsRequired
设置为true
:
[ProtoMember(1, IsRequired = true)]
还有一些其他方法可以告诉它始终发送值:
private bool ShouldSerializeIsPeriodic() { return true;}
(它使用的模式与core.NET支持的PropertyGrid
、XmlSerializer
、PropertyDescriptor
等模式相同-我不是在发明随机模式)
请注意,在“v2”中,我做了两个进一步的更改以帮助消除这种奇怪现象:
- 您可以选择绕过构造函数(WCF样式)
- 新的元模型提供了另一种决定是否假设默认值的方法
[ProtoContract]
public class MyOption
{
[ProtoMember(2)]
public View m_printListView = View.Details; (A)
[ProtoMember(5) ]
public bool m_bool = true ; (B)
}
void Main()
{
string fname = @"D:/test.dat";
if (File.Exists(fname) )
{
File.Delete(fname);
}
using(FileStream fs= new FileStream(fname, FileMode.OpenOrCreate, FileAccess.Write) )
{
MyOption opt = new MyOption();
opt.m_printListView = View.LargeIcon; // (1)
opt.m_bool = false; // (2)
Serializer.SerializeWithLengthPrefix(fs, opt, PrefixStyle.Fixed32);
}
using(FileStream fs= new FileStream(@"D:/test.dat", FileMode.Open, FileAccess.Read) )
{
MyOption opt;
opt = Serializer.DeserializeWithLengthPrefix<MyOption>(fs, PrefixStyle.Fixed32);
Console.WriteLine(opt.m_printListView);
Console.WriteLine(opt.m_bool);
}
}
请注意,在(A)和(B)中,我将默认值设置为View.Details和true。
在(1)和(2)中,在proto buf序列化和
反序列化,我们得到了错误的值
原因是:对于bool值,默认值为false,根据proto buf的设计原则,它将在可能的情况下节省空间,因此默认值不会保存在文件中,只保存一个标志,指示应使用默认值(我猜,未验证)
反序列化时,首先调用默认构造函数,在CLR运行时,行(B)实际上是默认构造函数的一部分,然后启动proto buf反序列化过程,发现成员m_bool有一个默认值标志,然后使用默认值false设置m_bool,覆盖(B)处的默认值
对于枚举类型,原因类似,在上面的示例视图中。LargeIcon是默认值,其数值为0(由反射验证)
要使用bool和enum成员的DefaultValueAttribute修复它,请执行以下操作:
[ProtoContract]
public class MyOption
{
[ProtoMember(2), DefaultValue(View.Details)]
public View m_printListView = View.Details; (A)
[ProtoMember(5), DefaultValue(true) ]
public bool m_bool = true ; (B)
}
对于枚举类型,还有其他问题:
第一个是所有枚举数都应该有不同的值,否则proto buf在序列化时会抛出异常,例如System.Drawing.RotateFlip枚举类型有以下定义:
public enum RotateFlipType
{
Rotate180FlipNone = 2,
Rotate180FlipX = 6,
Rotate180FlipXY = 0,
Rotate180FlipY = 4,
Rotate270FlipNone = 3,
Rotate270FlipX = 7,
Rotate270FlipXY = 1,
Rotate270FlipY = 5,
Rotate90FlipNone = 1,
Rotate90FlipX = 5,
Rotate90FlipXY = 3,
Rotate90FlipY = 7,
RotateNoneFlipNone = 0,
RotateNoneFlipX = 4,
RotateNoneFlipXY = 2,
RotateNoneFlipY = 6
}
从图像处理的角度来看,RotateNoneFlipNone和Rotate180FlipXY具有相同的效果,因此它们具有相同的效果
相同的基础值,这是一个合理的设计,但是,这样的枚举不能与proto buf一起工作:
The enum System.Drawing.RotateFlipType has conflicting values RotateNoneFlipNone and RotateNoneFlipNone
Serializer.ThrowInner (Exception exception)
at ProtoBuf.Serializer.ThrowInner(Exception exception)
at ProtoBuf.Serializer.Serialize[T](Stream destination, T instance)
at ProtoBuf.Serializer.SerializeWithLengthPrefix[T](Stream destination, T instance, PrefixStyle style, Int32 tag)
我的解决方法是创建自己的枚举,并在我的\u RotateLipType和System.Drawing.RotateLipType之间使用一对一映射。proto buf将仅序列化My_RotateFlipType
public enum RotateFlipType public enum My_RotateFlipType
{ {
Rotate180FlipNone = 2, Rotate180FlipNone,
Rotate180FlipX = 6, Rotate180FlipX,
Rotate180FlipXY = 0, Rotate180FlipXY,
Rotate180FlipY = 4, Rotate180FlipY,
Rotate270FlipNone = 3, Rotate270FlipNone,
Rotate270FlipX = 7, Rotate270FlipX,
Rotate270FlipXY = 1, Rotate270FlipXY,
Rotate270FlipY = 5, Rotate270FlipY,
Rotate90FlipNone = 1, Rotate90FlipNone,
Rotate90FlipX = 5, Rotate90FlipX,
Rotate90FlipXY = 3, Rotate90FlipXY,
Rotate90FlipY = 7, Rotate90FlipY,
RotateNoneFlipNone = 0, RotateNoneFlipNone,
RotateNoneFlipX = 4, RotateNoneFlipX,
RotateNoneFlipXY = 2, RotateNoneFlipXY,
RotateNoneFlipY = 6 RotateNoneFlipY
} }
为了避免手动同步两个数据成员,我使用ProtoBeforeSerialization和OnProtoAfterDeserialization功能将其自动化:
[ProtoAfterDeserialization()]
public void OnProtoAfterDeserialization()
{
Console.WriteLine("called OnProtoAfterDeserialization");
bool ret = Enum.TryParse(m_rotate.ToString(), out m_rotate_protobuf);
}
[ProtoBeforeSerialization()]
public void OnProtoBeforeSerialization()
{
Console.WriteLine("called OnProtoBeforeSerialization");
bool ret = Enum.TryParse(m_rotate_protobuf.ToString(), out m_rotate);
}
关于枚举的第二个问题是0值枚举数。如果枚举没有值为0的枚举数,则
protobuf很容易在运行时抛出异常
使用protobuf net时,我将遵循以下规则:
1.每当默认构造函数设置默认值以外的值时,请使用DefaultValueAttribute。
2.对于系统或第三方枚举类型,在将其添加到protobuf之前,请使用reflector(静态)或linq(运行时)检查它是否存在上述问题。如果发生冲突,请使用上述解决方法。我遇到了类似的问题,默认为
true
的bool
成员没有从配置文件中读取其值
[ProtoMember(1, IsRequired=true), DefaultValue(true)]
public bool IsPeriodic
...
[ProtoMember(1, IsRequired=true), DefaultValue(true)]
public bool IsPeriodic
...