StringBuilder.MoveChunks() in .NET 11: Warum mich das interessiert – und warum es die meisten meiner Kunden nicht sollte

Andrew Lock erklärt die neue StringBuilder.MoveChunks()-API in .NET 11 Preview 5. Ich erkläre, wann das für KMU-Entwickler relevant ist – und wann ihr eure Zeit besser investiert.

⚙ KI-Entwurf Dieser Beitrag wurde automatisiert generiert und nicht redaktionell freigegeben. Behandelt ihn entsprechend.

Letzte Woche saß ich bei einem Maschinenbauer aus dem Schwarzwald. Zehn Entwickler, .NET-Shop seit 15 Jahren, solide Codebasis. Der Lead-Dev zeigte mir ihren Report-Generator: ein monolithisches Stück Code, das pro Auftrag XML, PDF-Vorlagen und E-Mail-Bodies generiert. Alles in einem einzigen StringBuilder, der reihum durch vier verschiedene Methoden gereicht wird – und am Ende mit ToString() ausgewertet wird, bevor er direkt wieder gecleart und neu befüllt wird. Über Nacht läuft das Ding für mehrere tausend Aufträge durch.

Da dachte ich: Das ist genau der Anwendungsfall, den .NET 11 mit MoveChunks() adressiert. Oder? Fast.

Was Andrew Lock erklärt

Andrew Lock beschreibt in seinem Artikel eine neue statische Methode auf dem StringBuilder-Typ, die mit .NET 11 Preview 5 kommt: StringBuilder.MoveChunks(StringBuilder source). Die Methode "stiehlt" die internen Char-Buffer eines bestehenden StringBuilder-Objekts und packt sie in eine neue Instanz – ohne String-Allokation. Der ursprüngliche StringBuilder wird dabei auf einen sauberen Null-Zustand zurückgesetzt, verliert aber auch seine internen Buffer (die beim normalen Clear() noch gehalten werden, um sie wiederzuverwenden). Lock erklärt die Implementierung sauber und ehrlich: Sie ist technisch banal. Drei Zeilen. Was sie interessant macht, ist der Grund, warum sie existiert: Source Generators in Roslyn, also Code, der zur Compile-Zeit weiteren Code generiert. Dort gibt es Szenarien, in denen man einen StringBuilder nicht einfach als Referenz weitergeben kann, weil Frameworks die Instanz kontrollieren – und man trotzdem den Inhalt "herausziehen" will, ohne einen teuren ToString()-Aufruf zu riskieren, bevor der Text weiterverarbeitet wird.

Wie das in einem KMU mit 30–300 Mitarbeitern landet

Direkte Antwort: Für die meisten meiner Kunden ist MoveChunks() die falsche Frage zur richtigen Zeit.

Source Generators schreiben die wenigsten. Das ist die primäre Motivation hinter der API. Wer keinen Roslyn-Analyzer, kein Build-Time-Code-Gen und keine eigene Compiler-Extension entwickelt, wird MoveChunks() schlicht nie brauchen. Das ist kein Vorwurf – das ist ein Zielgruppen-Mismatch. Lock schreibt für eine Community, in der das Schreiben von Source Generators zum Werkzeugkasten gehört. Für den typischen .NET-Mittelständler ist das genauso relevant wie die Innereien von System.Numerics.Vector<T>.

Und die "echten" Performance-Szenarien? Wenn jemand bei mir sitzt und ein Problem mit String-Allokationen hat, dann sieht das meistens nicht aus wie ein elegantes Source-Generator-Problem. Es sieht aus wie der Schwarzwälder Maschinenbauer: irgendwo wird ein StringBuilder in einer Schleife mit new StringBuilder() neu erstellt, weil niemand daran gedacht hat, ihn zu recyclen. Oder es wird string + string + string in einem foreach gebaut, weil das mal "schnell hingeschrieben" wurde und jetzt 60.000 Mal pro Nacht läuft.

Das sind die Allokationsprobleme, die ich in KMU-Codebases wirklich antreffe. Kein MoveChunks(). Kein chunk-level pointer-swap. Sondern new StringBuilder() im Loop statt außerhalb – behoben in drei Minuten mit zehn Prozent weniger GC-Pressure.

Was wirklich fehlt in Locks Artikel – aus meiner Perspektive. Lock schreibt präzise für seinen Kontext. Was er nicht sagt (weil sein Publikum das weiß): Die ganze StringBuilder-Performance-Diskussion wird ab einem bestimmten Text-Volumen und einer bestimmten Architektur durch ArrayPool<char>, Span<T> und IBufferWriter<char> abgelöst. Wenn du ernsthaft über Allokationen in heißen Pfaden nachdenkst, ist StringBuilder oft schon die falsche Ebene. Aber das ist ein Thema für Hochfrequenz-Systeme – Fintech, Real-Time-APIs, Game-Server. Nicht für den Report-Generator meines Maschinenbauers.

Konkrete Empfehlung

Wenn du .NET-Entwickler im KMU bist oder ein Team führst, hier ist mein pragmatisches Triage-Schema:

Schritt 1 – Messe zuerst. Bevor du irgendetwas an StringBuilder-Logik anfasst, wirf BenchmarkDotNet oder den Memory-Profiler in Visual Studio an. Wenn Strings nicht in deinem Top-5-Allokations-Hotspot auftauchen, ist das kein Problem, das du lösen musst.

Schritt 2 – Die üblichen Verdächtigen beheben. new StringBuilder() innerhalb von Schleifen. string-Konkatenation in LINQ-Ausdrücken. ToString() auf Objektgraphen, die zehnmal pro Request gerendert werden. Das sind die echten Gewinne, die ich in der Praxis sehe – nicht neue Preview-APIs.

Schritt 3 – MoveChunks() auf dem Radar behalten, wenn du Builder-Patterns baust. Wenn du ein Framework-artiges System baust, bei dem unabhängige Komponenten in denselben Ausgabe-Stream schreiben (Reports, Template-Engines, Code-Generatoren mit Plugin-Architektur), dann lohnt sich ein zweiter Blick auf MoveChunks(), sobald .NET 11 GA ist. Das Muster – ein geteilter StringBuilder wird von mehreren unabhängigen Schreibern befüllt, und nach jedem Schreiber wird der Inhalt "herausgezogen" ohne den Buffer wegzuwerfen – das gibt es auch im Mittelstand. Selten, aber es gibt es.

Was ich dem Maschinenbauer empfohlen habe: Den StringBuilder außerhalb der Schleife halten, nach jedem Auftrag Clear() statt new StringBuilder() benutzen, und die vier Methoden so refactoren, dass sie alle auf dieselbe Instanz schreiben, statt jede ihren eigenen StringBuilder aufzumachen und am Ende zu konkatenieren. Kein Preview-5-Feature. Kein Breaking Change. Messbare Verbesserung.

MoveChunks() ist technisch elegant und für seinen Zweck richtig. Aber der Zweck ist spitz – und der liegt meistens nicht in eurer Codebasis.


Der vollständige Original-Beitrag von Andrew Lock: Avoiding ToString() allocations with StringBuilder.MoveChunks: Exploring the .NET 11 preview - Part 3


Wenn dein Team gerade eine ähnliche Diskussion führt – wo Performance-Optimierungen anfangen sollen, was Micro-Optimierung und was echter Hebel ist – dann komm gerne ins Klartext-Sparring. Kein Pitch, kein Deck. Nur 30 Minuten echte Fragen.

30 Min Klartext-Sparring

— Bernhard