NBMsgPack034: Async converters should not reuse MessagePackWriter after returning it
Custom converters (classes that derive from MessagePackConverter<T>) that override the WriteAsync method should not reuse a MessagePackWriter after returning it via ReturnWriter.
Example violation
In the example below, notice how ReturnWriter is called and then the returned writer is used again on the next statement.
public override async ValueTask WriteAsync(MessagePackAsyncWriter writer, SomeCustomType? value, SerializationContext context)
{
MessagePackWriter syncWriter = writer.CreateWriter();
if (value is null)
{
syncWriter.WriteNil();
writer.ReturnWriter(ref syncWriter);
return;
}
syncWriter.WriteArrayHeader(2);
syncWriter.Write(value.SeedCount);
writer.ReturnWriter(ref syncWriter); // syncWriter returned here
syncWriter.Write("Hi"); // OOPS: reused here
await writer.FlushIfAppropriateAsync(context);
}
Resolution
Rearrange the code so that all statements that use the writer occur before returning it.
public override async ValueTask WriteAsync(MessagePackAsyncWriter writer, SomeCustomType? value, SerializationContext context)
{
MessagePackWriter syncWriter = writer.CreateWriter();
if (value is null)
{
syncWriter.WriteNil();
writer.ReturnWriter(ref syncWriter);
return;
}
syncWriter.WriteArrayHeader(2);
syncWriter.Write(value.SeedCount);
syncWriter.Write("Hi");
writer.ReturnWriter(ref syncWriter);
await writer.FlushIfAppropriateAsync(context);
}
Note that the return must occur before and awaited expression. If the sync writer must be used after the await expression as well, you may create a new sync writer after the await statement.