我有一些模型对象,我保存在使用protobuf序列化的数据库中。我想将我将保存的版本与现有版本进行比较,以避免将相同版本添加两次。
理想情况下我应该
byte[] existingBlob = GetFromDBExistingModelObject();
ModelType existingModel = existingBlob.Deserialize();
if (!model.Equals(existingModel))
{
byte[] serializedModel = model.Serialize();
Save(serializedModel); //Save in DB the new blob
}
但是我必须在每个模型对象上实现.Equals
,这非常痛苦。我想做
byte[] existingBlob = GetFromDBExistingModelObject();
byte[] serializedModel = model.Serialize();
if (!compareBlob(existingBlob, serializedModel)
{
Save(serializedModel);
}
private bool compareBlob(byte[] existingBlob, byte[] serializedModel)
{
if (serializedModel.Length != existingBlob.Length)
{
return false;
}
return !serializedModel.Where((t, i) => t != existingBlob[i]).Any();
}
我也是为了表现,因为我没有反序列化existingBlob
您对此实施有何看法?你认为我可以依靠这种比较吗?我使用protobuf进行序列化。
感谢您的评论。
答案 0 :(得分:1)
protobuf-net 将产生可预测的输出,但严格来说不受规范保证; - 有两个边缘情况(字段顺序和子规范形式†用于varint编码)技术上可以产生具有相同含义的不同输出,但是protobuf-net将始终产生相同的输出。
我是玩弄故意使用子正常varint表单来避免一些内存改组,但这只是选择加入。
因此;只要你没有通过附加构建你的二进制文件(protobuf是一种可附加的格式,但显然所有的赌注都是关闭的,如果你以任意顺序附加),那么是:线上的数据应该是可预测的,你可以比较字节序列来测试是否相等。
作为次要注释,为了提高效率,我建议在此处使用常规for
循环:
if (serializedModel.Length != existingBlob.Length)
{
return false;
}
for(int i = 0 ; i < serializedModel.Length ; i++)
if(serializedModel[i] != existingBlob[i]) return false;
return true;
(如果您特别速度疯狂,您甚至可以使用unsafe
代码并将其作为int*
或long*
进行比较(取1 / 4或1/8的测试),并手动检查最后几个字节)
您也可以考虑比较 hash (sha1等)而不是逐字节;这对于大型模型尤其有用,特别是如果您可以将散列值与原始值一起存储(因此您永远不必获取原始的现有BLOB - 只是现有的散列)。
†:具体来说,varint“big end”的位序列10000000
或00000000
只是意味着“大端的零点更多”(有或没有更多数据要遵循),因此对数量没有影响;因此,varint末尾的任何(合理)数量的0x80 0x80 0x00
都不会改变结果;有一个用例,通过故意使用超大的varint作为长度前缀,可以用来避免必须移动数据。