Summary
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs implements every read/write primitive twice — once under #if NETCOREAPP using Span<byte>, ArrayPool<byte>, and ReadExactly, and once under #else using heap-allocated byte[] and stream.Read. The two implementations are structurally identical; the only differences are the allocation strategy and the ReadExactly vs manual-read pattern. The codebase already ships polyfills for these exact APIs in src/Polyfills/.
Duplication Details
Pattern: Dual #if NETCOREAPP / #else primitive read/write implementations
- Severity: Medium
- Occurrences: 12 methods duplicated (~87
#else lines mirroring ~140 #if NETCOREAPP lines)
- Location:
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (lines 30–257)
| Method |
#if NETCOREAPP lines |
#else lines |
ReadString |
31–46 |
172–181 |
ReadStringValue |
48–60 |
183–188 |
WriteString |
62–83 |
191–197 |
WriteSize<T> |
85–97 |
199–205 |
WriteInt |
99–106 |
207–211 |
ReadInt |
108–113 |
213–218 |
WriteLong |
115–121 |
220–224 |
ReadLong |
123–128 |
232–237 |
WriteUShort |
130–136 |
226–230 |
ReadUShort |
138–143 |
239–244 |
WriteBool |
145–151 |
246–250 |
ReadBool |
153–168 |
252–257 |
Example — WriteInt (signatures are 100% identical; only the body differs):
#if NETCOREAPP (stack-allocated Span<byte>):
protected static void WriteInt(Stream stream, int value)
{
Span<byte> bytes = stackalloc byte[sizeof(int)];
if (!BitConverter.TryWriteBytes(bytes, value))
{
throw Unreachable();
}
stream.Write(bytes);
}
#else (heap-allocated byte[]):
protected static void WriteInt(Stream stream, int value)
{
byte[] bytes = BitConverter.GetBytes(value);
stream.Write(bytes, 0, bytes.Length);
}
Example — ReadString:
#if NETCOREAPP uses ArrayPool<byte>.Shared.Rent/Return + stream.ReadExactly.
#else uses new byte[length] + stream.Read(bytes, 0, bytes.Length).
Impact Analysis
- Maintainability: Adding a new primitive (e.g.,
ReadUInt, WriteFloat) requires writing it twice. Any bug in a read/write primitive must be fixed in both branches.
- Bug Risk: The
#else branch's stream.Read calls do not assert/retry on short reads, while ReadExactly in #if NETCOREAPP does — a subtle behavioral divergence that could surface as data corruption on .NET Framework in low-level scenarios.
- Code Bloat: ~87 lines of duplicated code in the
#else branch alone.
Refactoring Recommendations
-
Use existing polyfills: The src/Polyfills/ directory already contains shims for Span<byte> and related APIs. Add or verify polyfills for:
Stream.ReadExactly(Span<byte>) — already shimmable via ReadExactly(byte[], int, int) plus a loop
BitConverter.TryWriteBytes(Span<byte>, int) — can be shimmed with BitConverter.GetBytes + Array.Copy
stream.Write(ReadOnlySpan<byte>) — can be shimmed with stream.Write(byte[], 0, length)
-
Collapse to a single implementation once the polyfills are in place, retaining the #if NETCOREAPP performance-optimized path as optional (or removing the #else branch entirely if .NET Framework is no longer a supported target for this assembly).
-
Fix the stream.Read short-read risk in the #else branch as a priority independent of the above, since it can produce silent data corruption.
Implementation Checklist
Analysis Metadata
- Analyzed Files: 1 (
BaseSerializer.cs)
- Detection Method: Semantic code analysis
- Analysis Date: 2026-07-09
🤖 Automated content by GitHub Copilot. Generated by the Duplicate Code Detector workflow. · 273.3 AIC · ⌖ 9.1 AIC · ⊞ 8K · [◷]( · ◷)
Add this agentic workflows to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
Summary
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.csimplements every read/write primitive twice — once under#if NETCOREAPPusingSpan<byte>,ArrayPool<byte>, andReadExactly, and once under#elseusing heap-allocatedbyte[]andstream.Read. The two implementations are structurally identical; the only differences are the allocation strategy and theReadExactlyvs manual-read pattern. The codebase already ships polyfills for these exact APIs insrc/Polyfills/.Duplication Details
Pattern: Dual
#if NETCOREAPP/#elseprimitive read/write implementations#elselines mirroring ~140#if NETCOREAPPlines)src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs(lines 30–257)#if NETCOREAPPlines#elselinesReadStringReadStringValueWriteStringWriteSize<T>WriteIntReadIntWriteLongReadLongWriteUShortReadUShortWriteBoolReadBoolExample —
WriteInt(signatures are 100% identical; only the body differs):#if NETCOREAPP(stack-allocatedSpan<byte>):#else(heap-allocatedbyte[]):Example —
ReadString:#if NETCOREAPPusesArrayPool<byte>.Shared.Rent/Return+stream.ReadExactly.#elseusesnew byte[length]+stream.Read(bytes, 0, bytes.Length).Impact Analysis
ReadUInt,WriteFloat) requires writing it twice. Any bug in a read/write primitive must be fixed in both branches.#elsebranch'sstream.Readcalls do not assert/retry on short reads, whileReadExactlyin#if NETCOREAPPdoes — a subtle behavioral divergence that could surface as data corruption on .NET Framework in low-level scenarios.#elsebranch alone.Refactoring Recommendations
Use existing polyfills: The
src/Polyfills/directory already contains shims forSpan<byte>and related APIs. Add or verify polyfills for:Stream.ReadExactly(Span<byte>)— already shimmable viaReadExactly(byte[], int, int)plus a loopBitConverter.TryWriteBytes(Span<byte>, int)— can be shimmed withBitConverter.GetBytes+Array.Copystream.Write(ReadOnlySpan<byte>)— can be shimmed withstream.Write(byte[], 0, length)Collapse to a single implementation once the polyfills are in place, retaining the
#if NETCOREAPPperformance-optimized path as optional (or removing the#elsebranch entirely if .NET Framework is no longer a supported target for this assembly).Fix the
stream.Readshort-read risk in the#elsebranch as a priority independent of the above, since it can produce silent data corruption.Implementation Checklist
Microsoft.Testing.Platformmust support on .NET FrameworkReadExactly,BitConverter.TryWriteBytes,Span<T>writeBaseSerializer.csto a single implementation using polyfillsstream.Readshort-read issue in the#elsebranch as an independent safety fix if the branch must remainAnalysis Metadata
BaseSerializer.cs)Add this agentic workflows to your repo
To install this agentic workflow, run