考慮如下聲明:
var t = new Protobuf0Len(){ A = false }
...
...
[ProtoBuf.ProtoContract(ImplicitFields = ProtoBuf.ImplicitFields.AllPublic)]
public class Protobuf0Len
{
public bool A { get; set; }
public IEnumerable<int> B { get; set; }
public int C { get; set; }
public string D { get; set; }
public long E { get; set; }
public DateTime? F { get; set; }
}
變量 t 的各個屬性都是默認的值,A = false, B = null, C = 0, D = null, E = 0, F = null.
用以下方法序列化這個 t, 你將得到一個 0 長度的 byte 數組:
public static byte[] Serialize(object obj)
{
if (obj == null)
return null;
using var msm = new MemoryStream();
Serializer.Serialize(msm, obj);
return msm.ToArray();
}
原因
因為, ProtoBuf 對這樣的數據不感興趣,都是默認值, 沒什么值得去序列化的。。。(沒找到官方說明, 我瞎寫的)
ProtoBuf 之所以這樣返回, 是沒有問題的, 因為0 長度的 byte 數組, 可以反序列化成萬物的:
ReadOnlySpan<byte> span = new byte[0];
var t1 = ProtoBuf.Serializer.Deserialize<Protobuf0Len>(span);
變量 t1 里的每個屬性都是默認值。。。
問題
但是,出于程序員特有的嚴謹, 我們拿到結果時, 都會去判斷一下是否非空, 非空時才會進行下一步操作。。。
比如:
// 鍋:
// {A=false, B=null}, 用 Protobuf 序列化后, 就是長度為 0 的字節數組.
// 寫到 redis 里,在讀出來, HasValue 就是 false,
// false 這里就不會執行了, 返回出去就是 null 了.
if (r.HasValue)
{
//var d = Deserialize<T>(r);
var d = await DeserializeAsync<T>(r);
results.Add(d);
}
這就造成了一個隱藏的BUG, 明明是有結果的, 但是卻取不出來。。。
解決方法
為了避免這個問題,序列化反序列化可以用:SerializeWithLengthPrefix
/ DeserializeWithLengthPrefix
var t = new Protobuf0Len();
byte[] a;
using (var msm = new MemoryStream())
{
ProtoBuf.Serializer.SerializeWithLengthPrefix<Protobuf0Len>(msm, t, ProtoBuf.PrefixStyle.Base128);
a = msm.ToArray();
}
using (var msm = new MemoryStream(a))
{
var t1 = ProtoBuf.Serializer.DeserializeWithLengthPrefix<Protobuf0Len>(msm, ProtoBuf.PrefixStyle.Base128);
}
這樣, 序列化出來的就至少是長度為1的 byte 數組了。