Modelli di immutabilità
Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 34 di 70.
L’immutabilità porta l’incapsulamento alle sue estreme conseguenze logiche: una volta creato, un oggetto non può mai cambiare. Questo elimina intere categorie di bug, perché non devi mai preoccuparti che qualcosa modifichi inaspettatamente i tuoi dati.
Il modo più semplice per creare una classe immutabile è usare campi readonly con un costruttore che imposta tutti i valori e non fornire setter:
public class Point
{
public readonly int X;
public readonly int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}Una volta creato un Point, le sue coordinate non possono cambiare. Ma cosa succede se ti serve una versione modificata? Invece di modificare l'oggetto esistente, ne restituisci uno nuovo:
public Point Move(int deltaX, int deltaY)
{
return new Point(X + deltaX, Y + deltaY);
}Il punto originale rimane invariato mentre ne ottieni uno nuovo con valori aggiornati. Questo schema viene utilizzato in tutto .NET: le stringhe, ad esempio, sono immutabili. Quando chiami ToUpper(), ottieni una nuova stringa anziché modificare quella originale.
Gli oggetti immutabili sono intrinsecamente sicuri per l'uso con più thread, poiché nessun codice può modificarli dopo la loro creazione. Sono anche più facili da comprendere, perché sai che il loro stato non cambierà mai in modo imprevisto.
Sfida
FacileCostruiamo un sistema monetario immutabile che mostri come si possono progettare oggetti che non cambiano mai dopo la creazione. Creerai una classe Money in cui ogni operazione restituisce un nuovo oggetto invece di modificare quello originale: un modello che rende il tuo codice più sicuro e più facile da comprendere.
Organizzerai il codice in due file:
Money.cs: crea una classeMoneyimmutabile nello spazio dei nomiFinance. Il tuo denaro deve avere:- Due campi
readonly:Amount(decimal) eCurrency(string) - Un costruttore che imposta entrambi i valori: una volta creato, un oggetto Money non può mai cambiare
- Un metodo
Add(decimal value)che restituisce un nuovo oggettoMoneycon l'importo aumentato (stessa valuta) - Un metodo
Subtract(decimal value)che restituisce un nuovo oggettoMoneycon l'importo diminuito (stessa valuta) - Un metodo
Convert(string newCurrency, decimal rate)che restituisce un nuovo oggettoMoneycon l'importo moltiplicato per il tasso e la nuova valuta - Un metodo
GetDisplay()che restituisce una stringa nel formato"{Amount} {Currency}"
Moneycompletamente nuove!- Due campi
Program.cs: nel file principale, mostra l'immutabilità in azione. Crea un oggettoMoneyiniziale, poi esegui delle operazioni mantenendo i riferimenti sia all'originale sia ai risultati. In questo modo dimostrerai che l'originale non cambia mai, nemmeno dopo più operazioni.
Riceverai quattro valori in ingresso:
- Importo iniziale (decimal)
- Valuta iniziale (string)
- Importo da aggiungere (decimal)
- Tasso di conversione in EUR (decimal)
Stampa l'output in questo formato:
Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}Ad esempio, se i valori in ingresso sono 100.00, USD, 50.00 e 0.85, l'output dovrebbe essere:
Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EURNota che l'oggetto Money originale mostra ancora 100.00 USD anche dopo aver chiamato Add(): questa è l'immutabilità! Il metodo Add() ha creato un oggetto completamente nuovo con 150.00 USD, lasciando intatto l'originale. La conversione viene eseguita sul risultato dell'operazione di addizione.
Provalo tu
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Leggi i dati di input
decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
string initialCurrency = Console.ReadLine();
decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
decimal conversionRate = Convert.ToDecimal(Console.ReadLine());
// TODO: Crea un oggetto Money iniziale con l'importo e la valuta iniziali
// TODO: Stampa l'importo originale usando GetDisplay()
// TODO: Chiama Add() sull'oggetto originale e memorizza il risultato in una nuova variabile
// TODO: Stampa il risultato dopo l'aggiunta usando GetDisplay()
// TODO: Stampa di nuovo l'oggetto originale per dimostrare che non è cambiato
// TODO: Converti il risultato dell'aggiunta in EUR usando il tasso di conversione
// TODO: Stampa l'importo convertito usando GetDisplay()
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniNamespace e direttiveIntroduzione a classi e oggettiLa parola chiave "this"Metodi e parametriCampi e proprietàCostruttoriInizializzatori di oggettiRiepilogo - Calcolatrice semplice4Ereditarietà
Sintassi di base dell'ereditarietà (:)La parola chiave 'base'Parole chiave virtual e overrideClassi sealedLa classe base 'object'Riepilogo - Gerarchia dei dipendenti7Funzionalità avanzate
Sovraccarico degli operatoriIndicizzatori (this[])Override di ToString()Metodi di estensioneRiepilogo - Lista personalizzata10Pattern di progettazione Parte 1
Introduzione ai pattern di progettazioneSingleton thread-safePattern FactoryPattern Observer (eventi)Pattern Strategy2Proprietà e membri statici
Proprietà implementate automaticamenteProprietà di sola lettura e di sola scritturaCampi e metodi staticiClassi staticheMembri con corpo di espressione5Polimorfismo e interfacce
Polimorfismo a compile time e runtimeInterfaccia o classe astrattaInterfacce multipleInterfacce espliciteUpcasting e downcastingRipasso: calcolatore di forme8Concetti avanzati di programmazione a oggetti
Composizione invece dell'ereditarietàTipi generici (classi e metodi)Delegati ed eventiAttributi e reflectionIDisposable e istruzione usingNozioni di base sull'iniezione delle dipendenze11Pattern di progettazione, parte 2
Pattern del ComandoPattern dell’AdattatorePattern del DecoratorePattern Template MethodPattern dello StatoPattern Composito3Architettura delle classi
Dati di istanza vs staticiParole chiave 'readonly' e 'const'Campi di supportoRiepilogo - Gestore di conti bancari6Incapsulamento
Modificatori di accessoProprietà per l'incapsulamentoImplementazione dell'occultamento dei datiModelli di immutabilitàRiepilogo - Registri degli studentiEsercitati da solo: Compilatore C# online