Einführung: Warum C#-Leistung wichtig ist
C# und die .NET-Plattform sind leistungsstarke Werkzeuge für den Bau hochbelasteter Webdienste, Desktop-Anwendungen und Unity-Spiele. Allerdings kann selbst der eleganteste Code langsam laufen, wenn Sie die Besonderheiten der Funktionsweise von CLR (Common Language Runtime), Garbage Collector (GC) und JIT-Kompilierungsmechanismen nicht berücksichtigen.
In diesem Artikel werden wir wichtige Methoden zur Optimierung der Leistung von C#-Anwendungen aufschlüsseln, die Ihnen helfen, die Antwortzeit zu verkürzen, den Speicherverbrauch zu senken und den Systemdurchsatz zu erhöhen. Alle Tipps werden von praktischen Codebeispielen begleitet.
1. Speicherverwaltung und Garbage Collection (GC)
Eines der Hauptmerkmale von .NET ist die automatische Speicherverwaltung. Die unsachgemäße Verwendung von Objekten führt jedoch zu häufigen Garbage Collections, was zu Anwendungs-"Pausen" (Einfrieren) führt.
1.1. Vermeiden Sie Allokationen in heißen Pfaden
Versuchen Sie, in Schleifen oder häufig aufgerufenen Methoden keine neuen Objekte zu erstellen. Verwenden Sie stattdessen Objektpools oder verwenden Sie Instanzen wieder.
// Schlecht: Bei jeder Iteration wird ein neuer StringBuilder erstelltfor (int i = 0; i < 1000; i++){ var sb = new StringBuilder(); sb.Append(i);}
// Gut: Pool verwenden oder wiederverwendenvar sbPool = new StringBuilder();for (int i = 0; i < 1000; i++){ sbPool.Clear(); sbPool.Append(i);}1.2. Verwenden Sie Strukturen anstelle von Klassen für kleine Daten
Strukturen werden auf dem Stapel gespeichert und belasten den GC nicht. Aber Vorsicht: Große Strukturen (mehr als 16 Byte) können beim Kopieren langsam sein.
public struct Point{ public int X; public int Y;}
// Der Methodenaufruf erzeugt nun kein Objekt auf dem Heapvoid ProcessPoint(Point p) { /* ... */ }1.3. Aktivieren Sie den Server-GC-Modus für Serveranwendungen
Standardmäßig verwenden Konsolenanwendungen den Workstation-GC. Für Webdienste (ASP.NET Core) wechseln Sie in der csproj-Datei zum Server-GC:
<PropertyGroup> <ServerGarbageCollection>true</ServerGarbageCollection></PropertyGroup>2. Optimierung der Arbeit mit LINQ und Sammlungen
LINQ ist ein praktisches, aber potenziell langsames Werkzeug, wenn Sie seine "Fallstricke" nicht kennen.
2.1. Vermeiden Sie mehrere Enumerationen
Jeder Aufruf von .Count(), .ToList() oder .Any() kann die Abfrage erneut ausführen. Speichern Sie das Ergebnis in einer Variablen.
// Schlecht: Zwei Durchläufe über die Sammlungif (items.Where(x => x.IsActive).Count() > 0) { /* ... */ }var activeList = items.Where(x => x.IsActive).ToList();
// Gut: Ein Durchlaufvar activeItems = items.Where(x => x.IsActive).ToList();if (activeItems.Count > 0) { /* ... */ }2.2. Verwenden Sie spezialisierte Sammlungen
Für häufige schlüsselbasierte Nachschlagevorgänge verwenden Sie Dictionary<TKey, TValue> anstelle von List<T>. Für "First in, first out"-Operationen verwenden Sie Queue<T>.
// Schneller Zugriff: O(1) statt O(n)var dict = new Dictionary<int, string>();dict.Add(1, "eins");if (dict.ContainsKey(1)) { /* ... */ }2.3. Ersetzen Sie LINQ in kritischen Abschnitten durch Schleifen
Bei einfachen Operationen (Summe, Filtern) kann eine for- oder foreach-Schleife 2-3 mal schneller sein als LINQ.
// LINQ (langsam)int sum = numbers.Sum();
// Schleife (schnell)int sum = 0;for (int i = 0; i < numbers.Length; i++){ sum += numbers[i];}3. Asynchrone Programmierung und Reduzierung von Blockierungen
Die korrekte Verwendung von async/await ermöglicht es Ihnen, Threads nicht zu blockieren, was für die Skalierbarkeit entscheidend ist.