Skip to content

BinaryWriter breaking change #107265

@koszeggy

Description

@koszeggy

Description

When using BinaryWriter.Write(string), it attempts to optimize the performance, and when the value is short enough (and the encoding is UTF8), then the operation may bypass the Write7BitEncodedInt call.

But this is a problem if we use a derived BinaryWriter where the Write(int) method is overridden because the Write7BitEncodedInt method calls the virtual Write(int) method.

Example: we have a pair of reader-writer that dumps the written/read values while adjusts the current position. Since BinaryReader always calls the Read7BitEncodedInt method, the dumped information and offsets slowly drift away when short strings are written:

image

Reproduction Steps

  • Create a derived BinaryWriter with an overridden Write(int) method.
  • Call Write(string) with various lengths
  • Observe that the overridden Write(int) is sometimes called, sometimes not, depending on string length.

Expected behavior

BinaryWriter and BinaryReader should have a symmetric behavior. When we have a derived BinaryWriter class, I always expect the overridden Write(int) method to be called because that's how BinaryReader also works (ie. its ReadString always calls ReadInt32).

Actual behavior

BinaryWriter.Write(string) sometimes calls the implicit Write(int) method, sometimes not. See the details above.

Regression?

It works in .NET Framework, and it worked in .NET 5.0.

Known Workarounds

Duplicating the old behavior in a derived class without calling base.Write(string) (a bit painful).

Configuration

Noticed in .NET 9 Preview 7 but it turns out that we have this behavior since .NET 6.0

Other information

Possible solutions:

  • Check in the base constructor if the current type is a derived one, and if so, then initialize the _useFastUtf8 field to false.
  • Or, allow only the "semi-optimized" branch, which still calls the Write7BitEncodedInt method,

Metadata

Metadata

Assignees

Labels

area-System.IOin-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions