Einleitung: Warum Legacy-Code ein Refactoring benötigt?
\n\nJeder Entwickler, der mit der .NET-Plattform arbeitet, stößt früher oder später auf Legacy-Code. Das sind Programme, die in älteren Versionen von C# (z. B. .NET Framework 4.x) geschrieben wurden, veraltete Muster wie DataTable in ASP.NET Web Forms oder monolithische Klassen mit tausenden von Zeilen verwenden. Solcher Code ist eine Quelle technischer Schulden: Er ist schwer zu warten, enthält versteckte Fehler und bremst die Einführung neuer Funktionen.
Refactoring ist der Prozess der Änderung der internen Struktur von Code, ohne sein externes Verhalten zu verändern. In diesem Artikel werden wir einen schrittweisen Plan für das Refactoring von Legacy-Code in C# durchgehen, von der Diagnose bis zur Einführung moderner Praktiken. Sie erfahren, wie Sie ein altes Projekt mit .NET-Tools und bewährten Techniken sicher verbessern können.
\n\n1. Diagnose und Vorbereitung: Wo anfangen?
\n\nBevor Sie den Code ändern, müssen Sie seinen aktuellen Zustand verstehen. Ohne Analyse riskieren Sie, die Funktionalität zu beschädigen.
\n\n1.1. Bewertung der technischen Schulden
\nVerwenden Sie Code-Analysatoren, die in Visual Studio integriert oder über NuGet verfügbar sind:
\n- \n
- Roslyn-Analysatoren — finden potenzielle Fehler und veraltete Konstrukte. \n
- SonarQube (oder SonarCloud) — bewertet Codequalität, Duplikate und Komplexität. \n
- NDepend — visualisiert Abhängigkeiten zwischen Klassen und Modulen. \n
1.2. Schreiben von Tests als Sicherheitsnetz
\nRefactoring ohne Tests ist russisches Roulette. Bei Legacy-Code gibt es oft keine Unit-Tests. Beginnen Sie mit dem Schreiben von Charakterisierungstests (characterization tests), die das aktuelle Verhalten festhalten:
\n\n// Beispieltest für eine alte Methode\n[Test]\npublic void CalculateDiscount_ShouldReturn10Percent_WhenOrderTotalIsGreaterThan100()\n{\n // Arrange\n var legacyClass = new OrderProcessor();\n var order = new Order { Total = 150 };\n \n // Act\n var result = legacyClass.CalculateDiscount(order);\n \n // Assert\n Assert.AreEqual(15, result); // 10% von 150\n}\n\nErstellen Sie Tests für kritische Pfade. So können Sie sicher refactoren, da Sie wissen, dass sich das Verhalten nicht ändert.
\n\n1.3. Strategiewahl: "Die Gartenstrategie"
\nVersuchen Sie nicht, das gesamte Projekt auf einmal zu refactoren. Wählen Sie ein Modul oder eine Klasse aus, die die meisten Probleme verursacht (hohe Komplexität, häufige Fehler). Arbeiten Sie iterativ: kleine Schritte, häufige Commits.
\n\n2. Wichtige Refactoring-Techniken für Legacy-Code in C#
\n\nBetrachten wir die wichtigsten Methoden, um "Spaghetti-Code" in eine saubere Architektur zu verwandeln.
\n\n2.1. Beseitigung von God-Objekten (große Klassen)
\nLegacy-Code enthält oft Klassen, die alles machen: mit der Datenbank arbeiten, Daten validieren, E-Mails senden. Verwenden Sie das Single-Responsibility-Prinzip (SRP).
\n\nVor dem Refactoring:
\npublic class UserService\n{\n public void RegisterUser(string name, string email)\n {\n // Validierung\n if (string.IsNullOrEmpty(name)) throw new Exception();\n \n // Datenbankarbeit\n using (var conn = new SqlConnection("..."))\n {\n conn.Execute("INSERT INTO Users ...");\n }\n \n // E-Mail-Versand\n SmtpClient client = new SmtpClient();\n client.Send(new MailMessage(...));\n }\n}\n\nNach dem Refactoring:
\npublic class UserService\n{\n private readonly IUserRepository _userRepository;\n private readonly IEmailService _emailService;\n private readonly IValidator<UserRegistration> _validator;\n\n public UserService(IUserRepository repo, IEmailService email, IValidator<UserRegistration> validator)\n {\n _userRepository = repo;\n _emailService = email;\n _validator = validator;\n }\n\n public void RegisterUse