Einführung: Warum Leistung von den ersten Codezeilen an wichtig ist
Jeder Entwickler, auch ein Anfänger, gerät in Situationen, in denen eine einfache Anwendung langsamer wird, zu viel Speicher verbraucht oder lange für die Verarbeitung von Anfragen benötigt. Die Optimierung der Leistung von C#-Anwendungen ist keine Magie und nicht nur die Aufgabe von Senior-Entwicklern. Es ist eine Reihe praktischer Regeln und Werkzeuge, die in jeder Lernphase beherrscht werden können.
In diesem Artikel analysieren wir die wichtigsten Aspekte der Optimierung von C# (.NET)-Code. Sie erfahren, wie Sie häufige Anfängerfehler vermeiden, die Arbeit mit Speicher, Zeichenketten und Sammlungen beschleunigen und Multithreading richtig einsetzen. Das Material richtet sich an Entwickler, die bereits mit den Grundlagen der C#-Syntax vertraut sind und effizienteren Code schreiben möchten.
1. Arbeiten mit Zeichenketten: Warum String schlecht und StringBuilder gut ist
Eines der häufigsten Leistungsprobleme in C# ist die falsche Behandlung von Zeichenketten. Zeichenketten in .NET sind unveränderlich. Das bedeutet, dass jede Verkettungsoperation ein neues String-Objekt im Speicher erstellt und das alte Objekt für die Garbage Collection übrig lässt.
Das Problem: Verkettung in einer Schleife
Betrachten Sie ein schlechtes Beispiel:
string result = "";for (int i = 0; i < 1000; i++){ result += i.ToString() + " ";}// Dies erzeugt 1000 Zwischenstrings!Jede Schleifeniteration erzeugt einen neuen String. Bei 1000 Iterationen sind das 1000 Speicherzuweisungen. Bei 100.000 Iterationen kann die Anwendung einfrieren oder eine OutOfMemoryException auslösen.
Die Lösung: StringBuilder verwenden
Die Klasse StringBuilder arbeitet mit einem veränderlichen Zeichenpuffer. Sie erzeugt keine neuen Objekte, wenn Text hinzugefügt wird.
using System.Text;
StringBuilder sb = new StringBuilder();for (int i = 0; i < 1000; i++){ sb.Append(i); sb.Append(" ");}string result = sb.ToString();Tipp: Verwenden Sie immer StringBuilder, wenn Sie mehr als 3-5 Verkettungsoperationen durchführen, insbesondere innerhalb von Schleifen. Für die einfache Verkettung weniger Zeichenketten (z.B. string s = a + b + c;) optimiert der C#-Compiler dies zu einem einzigen string.Concat-Aufruf, daher ist StringBuilder hier nicht erforderlich.
2. LINQ optimieren: Die richtigen Methoden auswählen
LINQ (Language Integrated Query) ist ein leistungsstarkes Werkzeug, aber seine falsche Verwendung kann eine Anwendung erheblich verlangsamen. Das Hauptproblem ist die mehrfache Aufzählung von Sammlungen.
Beispiel einer "teuren" LINQ-Abfrage
var numbers = Enumerable.Range(1, 1000000);var countEven = numbers.Where(n => n % 2 == 0).Count();// Hier erzeugt Where() einen Iterator, Count() iteriert darüber.// Das ist in Ordnung, wenn die Sammlung einmal aufgezählt wird.Ein Fehler tritt auf, wenn Sie mehrere Terminaloperationen (Count, ToList, First, Any) auf demselben LINQ-Ausdruck aufrufen:
var query = numbers.Where(n => n % 2 == 0);int count1 = query.Count(); // Erster Durchlaufint count2 = query.Count(); // Zweiter Durchlauf!// Die Sammlung wird zweimal aufgezählt.Jeder Aufruf von Count() oder ToList() führt die Filterung erneut aus. Wenn Sie eine komplexe Filterung haben oder die Datenquelle eine Datenbank (Entity Framework) ist, führt dies zu doppelten Abfragen.
Der richtige Ansatz: Materialisierung
Wenn Sie das Abfrageergebnis mehrfach verwenden müssen, speichern Sie es in einer Liste oder einem Array:
var query = numbers.Where(n => n % 2 == 0).ToList(); // Ein Durchlaufint count1 = query.Count;int count2 = query.Count;Tipp: Verwenden Sie .ToList() oder .ToArray() sofort nach der Filterung, wenn Sie planen, mehrere Methoden aufzurufen (Count, First, Sum). Für einen einzelnen Aufruf lassen