C#: Undo și Redo pentru modificări controlate
Implementează Undo și Redo în C# cu două stive. Înțelege istoricul, ștergerea ramurii redo după o modificare nouă și diferența dintre stare și comandă.
În acest material
O aplicație de editare trebuie să poată reveni asupra unei modificări fără să recalculeze întreaga sesiune. Pentru un model mic, păstrăm stări anterioare într-o stivă undo și stări recuperabile într-o stivă redo. Modelul este un text; pentru obiecte mutable ar fi necesare copii independente sau comenzi inverse bine definite.
Cum lucrezi cu exemplul
Programul complet folosește C# 7.3 și API-uri disponibile în .NET Framework 4.8. Deschide Lesson.csproj din arhivă sau creează un proiect Console App (.NET Framework 4.8) și înlocuiește Program.cs. Datele de demonstrație sunt incluse în cod.
Descarcă sursele și instrucțiunile.
Ideea și pașii de implementare
Set pune valoarea curentă în undo, aplică valoarea nouă și golește redo. Undo mută starea curentă în redo și extrage starea anterioară. Redo face operația simetrică. O modificare nouă după Undo creează o ramură nouă, deci vechiul viitor nu mai este disponibil.
Nu salvăm un pas dacă valoarea nouă este identică cu cea curentă. Pentru texte mari sau multe obiecte, copiile complete consumă memorie; poți limita istoricul sau folosi modelul Command, în care fiecare operație știe cum să se execute și să se inverseze.
Exemplu și rezultat așteptat
Pornim de la A, modificăm în B și apoi în C. Undo revine la B; încă un Undo revine la A. Redo reface B. Modificarea în X golește redo, iar o nouă încercare de Redo este refuzată. Programul afișează B, A, B, X și False.
A → B → C → Undo → B → Redo → C → Editare nouă → golește redo.Mărește schema ↗
Codul complet
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Globalization;
using System.Xml.Linq;
using System.Data;
class History {
readonly Stack<string> undo=new Stack<string>(),redo=new Stack<string>();
public string Value {get;private set;}
public History(string value) {Value=value;}
public void Set(string value) {if(value==Value)return;undo.Push(Value);Value=value;redo.Clear();}
public bool Undo() {if(undo.Count==0)return false;redo.Push(Value);Value=undo.Pop();return true;}
public bool Redo() {if(redo.Count==0)return false;undo.Push(Value);Value=redo.Pop();return true;}
}
class Program {
static void Main() {
var h=new History("A");h.Set("B");h.Set("C");h.Undo();Console.WriteLine(h.Value);
h.Undo();Console.WriteLine(h.Value);h.Redo();Console.WriteLine(h.Value);
h.Set("X");Console.WriteLine(h.Value);Console.WriteLine(h.Redo());
}
}
Rezultatul programului:
B
A
B
X
False
Greșeli și limite
Nu păstra în istoric aceeași referință la o listă modificată continuu: toate „stările” ar putea arăta identic. Nu înregistra o operație eșuată ca pas valid. Persistarea în baza de date și Undo local sunt responsabilități diferite; o anulare vizuală nu anulează automat o tranzacție deja confirmată.
Exersează
Testează Undo la început, Redo fără Undo și trei modificări identice. Istoricul nu trebuie să producă excepții sau pași inutili. Leagă modelul la două butoane și dezactivează-le când stivele respective sunt goale. Explică de ce o editare nouă după Undo trebuie să golească redo.
Continuă parcursul
Parcursul complet de 50 de lecții
Lecția precedentă: C#: resurse localizate și texte fără concatenări fragile
Lecția următoare: C#: miniaplicație quiz cu răspuns unic și scor.
Referință: documentația Microsoft.
Întrebări frecvente
De ce folosesc două stive?
Una păstrează trecutul accesibil, cealaltă păstrează stările anulate care pot fi refăcute.
Pot salva doar referința la un obiect mutabil?
Nu dacă obiectul va fi modificat ulterior. Ai nevoie de copie sau de o comandă inversă corectă.
Undo local anulează automat SQL?
Nu. Trebuie proiectată explicit relația dintre istoricul interfeței și operațiile persistate.
Un pas mai departe
Autentifică-te pentru a păstra progresul în cont.