Immutability-Muster
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 34 von 70.
Unveränderlichkeit führt die Kapselung zu ihrem logischen Extrem – sobald ein Objekt erstellt wurde, kann es sich nie mehr ändern. Dies eliminiert ganze Kategorien von Fehlern, da Sie sich nie Sorgen machen müssen, dass etwas Ihre Daten unerwartet ändert.
Der einfachste Weg, eine unveränderliche Klasse zu erstellen, besteht darin, readonly-Felder mit einem Konstruktor zu verwenden, der alle Werte setzt, und keine Setter bereitzustellen:
public class Point
{
public readonly int X;
public readonly int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}Sobald ein Point erstellt wurde, können seine Koordinaten nicht mehr geändert werden. Aber was ist, wenn Sie eine modifizierte Version benötigen? Anstatt das bestehende Objekt zu ändern, geben Sie ein neues zurück:
public Point Move(int deltaX, int deltaY)
{
return new Point(X + deltaX, Y + deltaY);
}Der ursprüngliche Punkt bleibt unverändert, während Sie einen neuen Punkt mit aktualisierten Werten erhalten. Dieses Muster wird in ganz .NET verwendet – Strings zum Beispiel sind unveränderlich. Wenn Sie ToUpper() aufrufen, erhalten Sie einen neuen String, anstatt den ursprünglichen zu ändern.
Unveränderliche Objekte sind von Natur aus threadsicher, da kein Code sie nach der Erstellung modifizieren kann. Sie sind zudem einfacher zu verstehen, da man weiß, dass sich ihr Zustand niemals unerwartet ändern wird.
Aufgabe
EinfachLassen Sie uns ein unveränderliches (immutable) Geldsystem aufbauen, das zeigt, wie Objekte so entworfen werden können, dass sie sich nach der Erstellung nie mehr ändern. Sie werden eine Money-Klasse erstellen, bei der jede Operation ein neues Objekt zurückgibt, anstatt das ursprüngliche zu ändern – ein Muster, das Ihren Code sicherer und leichter nachvollziehbar macht.
Sie werden Ihren Code in zwei Dateien organisieren:
Money.cs: Erstellen Sie eine unveränderlicheMoney-Klasse im NamespaceFinance. Ihr Geld-Objekt sollte Folgendes haben:- Zwei
readonly-Felder:Amount(decimal) undCurrency(string) - Einen Konstruktor, der beide Werte setzt – einmal erstellt, kann sich ein Money-Objekt nie mehr ändern
- Eine Methode
Add(decimal value), die ein neuesMoney-Objekt mit dem erhöhten Betrag (gleiche Währung) zurückgibt - Eine Methode
Subtract(decimal value), die ein neuesMoney-Objekt mit dem verringerten Betrag (gleiche Währung) zurückgibt - Eine Methode
Convert(string newCurrency, decimal rate), die ein neuesMoney-Objekt zurückgibt, bei dem der Betrag mit dem Kurs multipliziert wurde und die neue Währung gesetzt ist - Eine Methode
GetDisplay(), die einen String im Format"{Amount} {Currency}"zurückgibt
Money-Instanzen zurück!- Zwei
Program.cs: Demonstrieren Sie in Ihrer Hauptdatei die Unveränderlichkeit in Aktion. Erstellen Sie ein anfänglichesMoney-Objekt und führen Sie dann Operationen damit aus, während Sie Referenzen sowohl auf das Original als auch auf die Ergebnisse behalten. Dies wird beweisen, dass sich das Original auch nach mehreren Operationen nie ändert.
Sie erhalten vier Eingaben:
- Anfangsbetrag (decimal)
- Anfangswährung (string)
- Hinzuzufügender Betrag (decimal)
- Umrechnungskurs zu EUR (decimal)
Geben Sie die Ausgabe in diesem Format aus:
Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}Wenn die Eingaben beispielsweise 100.00, USD, 50.00 und 0.85 sind, sollte die Ausgabe wie folgt aussehen:
Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EURBeachten Sie, dass das ursprüngliche Money-Objekt auch nach dem Aufruf von Add() immer noch 100.00 USD anzeigt – das ist Unveränderlichkeit! Die Methode Add() hat ein völlig neues Objekt mit 150.00 USD erstellt und das Original unberührt gelassen. Die Umrechnung wird auf das Ergebnis der Add-Operation angewendet.
Spickzettel
Unveränderlichkeit (Immutability) bedeutet, dass ein Objekt nach seiner Erstellung niemals geändert werden kann. Dies eliminiert Fehler, die mit unerwarteten Datenänderungen zusammenhängen.
Erstellen Sie eine unveränderliche Klasse unter Verwendung von readonly-Feldern mit einem Konstruktor und ohne Setter:
public class Point
{
public readonly int X;
public readonly int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}Um ein unveränderliches Objekt zu modifizieren, geben Sie eine neue Instanz zurück, anstatt die bestehende zu ändern:
public Point Move(int deltaX, int deltaY)
{
return new Point(X + deltaX, Y + deltaY);
}Vorteile von unveränderlichen Objekten:
- Thread-sicher durch Design, da kein Code sie nach der Erstellung ändern kann
- Einfacher nachzuvollziehen, da sich ihr Zustand nie unerwartet ändert
- Werden überall in .NET verwendet (z. B. sind Strings unveränderlich)
Probier es selbst
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Eingaben lesen
decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
string initialCurrency = Console.ReadLine();
decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
decimal conversionRate = Convert.ToDecimal(Console.ReadLine());
// TODO: Erstelle ein initiales Money-Objekt mit dem ursprünglichen Betrag und der Währung
// TODO: Gib das ursprüngliche Geld mit GetDisplay() aus
// TODO: Rufe Add() auf dem Original auf und speichere das Ergebnis in einer neuen Variable
// TODO: Gib das Ergebnis nach dem Hinzufügen mit GetDisplay() aus
// TODO: Gib das Original erneut aus, um zu beweisen, dass es unverändert ist
// TODO: Konvertiere das addierte Ergebnis unter Verwendung des Umrechnungskurses in EUR
// TODO: Gib das konvertierte Geld mit GetDisplay() aus
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten