C # how to use a generic container Stack <T> and enable the "undo/Redo" function,
. Net provides many generic collections for us. For example, Stack <T> comes first, Queue <T> comes first, and List <T> sets can be sorted. Indexes, sorted List <T>, and bidirectional linked List can be implemented in a generic way, indexes are not supported. ISet <T> cannot be copied. It has two implementations: HashSet <T>, which does not maintain the sorting of set elements, and SortedSet <T>, supports sorting of collection elements. IDictionary <TKey, TValue> is a generic interface for dictionary sets. SortedList <TKey, TValue> implements IDictionary <TKey, TValue>, but it is also a set that maintains the sorting of the Set elements and supports keys or value-based indexes.
This article describes how to use a Stack <T>.
Basic usage
Stack <T> is a Stack generic implementation. It provides several methods and attributes, such as the number of elements in the Stack, the number of elements in the Stack, and the number of elements in the Stack. The biggest feature of the stack is that the stack can be imagined as a stacked plate. In the stack, each plate is placed on the top, and the stack is removed from the top. Easy to use:
class Program
{
static void Main(string[] args)
{
Var customer1 = new Customer () {ID = 1, Name = "Zhang San", Gender = "male "};
Var customer2 = new Customer () {ID = 2, Name = "", Gender = "male "};
Stack<Customer> stackCustomers = new Stack<Customer>();
// Stack entry
stackCustomers.Push(customer1);
stackCustomers.Push(customer2);
// View the top element of the stack
Customer topCustomer = stackCustomers.Peek();
Console. WriteLine ("top stack element:" + topCustomer. Name );
// Traverse all the elements in the stack
foreach (var customer in stackCustomers)
{
Console.WriteLine("id is {0},name is {1}", customer.ID, customer.Name);
}
// Output Stack
Customer outCustomer = stackCustomers.Pop();
Console. WriteLine ("the stack is being output:" + outCustomer. Name );
Console. WriteLine ("number of elements in the current stack:" + stackCustomers. Count );
Console.ReadKey();
}
}
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
Copy a generic Stack <T>
The generic Stack class maintains this generic array and index pointer internally, and the initial position of the pointer is-1.
In the stack, the pointer is directed to the premise and the elements in the stack are assigned to the position in the stack. In addition, whether the upper limit of capacity is reached should be considered for the inbound stack. If the upper limit is reached, the array should be resized.
The output stack is used to set the element value of the current stack position to the default value of the inbound stack type, and remove a large pointer.
Obtaining the top element of the stack is the element corresponding to the current index position of the stack.
public class MyStack<T>
{
// Maintain the T-type array
private T[] _elements;
protected T[] Elements
{
get { return _elements; }
set { _elements = value; }
}
public MyStack()
{
_ Capacity = 5; // Initial Value
Elements = new T[Capacity];
}
public MyStack(int capacity)
{
Capacity = capacity;
Elements = new T[Capacity];
}
// Pointer
private int _index = -1;
public int Index
{
get { return _index; }
set { _index = value; }
}
// Capacity
private int _capacity;
public int Capacity
{
get { return _capacity; }
set { _capacity = value; }
}
// Length = index + 1
public int Length
{
get { return Index + 1; }
}
// Stack entry
public void Push(T element)
{
if (this.Length == Capacity)
{
IncreaseCapacity();
}
Index++;
Elements[Index] = element;
}
// Output Stack
public T Pop()
{
if (this.Length < 1)
{
Throw new InvalidOperationException ("empty in stack ");
}
T element = Elements[Index];
// The original position element is changed to the default value
Elements[Index] = default(T);
// Subtract one from the index
Index--;
return element;
}
// Obtain the top element of the stack
public T Peek()
{
if (this.Length < 1)
{
Throw new InvalidOperationException ("empty in stack ");
}
return Elements[Index];
}
private void IncreaseCapacity()
{
Capacity++;
Capacity *= 2;
// Create a new T-type array
T[] newElements = new T[Capacity];
// Copy the original array to the new array
Array.Copy(Elements, newElements, Elements.Length);
Elements = newElements;
}
}
Now, a series of inbound and outbound operations are performed on the client.
static void Main(string[] args)
{
// Create a generic Stack instance
MyStack<int> myStack = new MyStack<int>();
// Traverse the stack for 10 times
for (int i = 0; i < 10; i++)
{
Console. WriteLine (I + "starting to stack ");
myStack.Push(i);
Console. WriteLine ("the current stack Length is:" + myStack. Length );
}
// Traverse the stack 10 times
for (int i = 0; i < 10; i++)
{
Console. WriteLine ("current output stack is" + myStack. Peek ());
myStack.Pop();
Console. WriteLine ("the current stack Length is:" + myStack. Length );
}
// When all outbound stacks end, check the top element of the stack and throw an exception.
try
{
myStack.Peek();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
}
// End all outbound stacks and then throw an exception
try
{
myStack.Pop();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
In fact, the <T> of the generic Stack also maintains an array, and the array capacity changes dynamically. This is similar to List <T>, as mentioned here.
Use a generic Stack <T> to perform the "undo/Redo" Operation
First, the operation or undo operation is to extract an interface for certain types of undo or redo operations.
public interface ICommand<T>
{
T Do(T input);
T Undo(T input);
}
Assume that you want to "undo/Redo" an integer.
public class AddIntCommand : ICommand<int>
{
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
public AddIntCommand()
{
_value = 0;
}
public AddIntCommand(int value)
{
_value = value;
}
// Perform the operation
public int Do(int input)
{
return input + _value;
}
// Cancel the operation
public int Undo(int input)
{
return input - _value;
}
}
Next, you need a generic class to manage all the undo or operation commands and place these commands in the Stack <ICommand <T> generic collection.
// Undo or redo with generic Stack
public class UndoRedoStack<T>
{
Private Stack <ICommand <T> _ undo; // undo-related generic stack
Private Stack <ICommand <T> _ redo; // generic stack related to redo
public UndoRedoStack()
{
Reset();
}
// Number of records revoked
public int UndoCount
{
get { return _undo.Count; }
}
// Record the number of retries
public int RedoCount
{
get { return _redo.Count; }
}
// Restore to factory settings
public void Reset()
{
_undo = new Stack<ICommand<T>>();
_redo = new Stack<ICommand<T>>();
}
// Perform the operation
public T Do(ICommand<T> cmd, T input)
{
T output = cmd.Do(input);
// Put the command in the Undo stack.
_undo.Push(cmd);
// Once a new command is started, the redo stack is cleared.
_redo.Clear();
return output;
}
// Cancel the operation
public T Undo(T input)
{
if (_undo.Count > 0)
{
// Output Stack
ICommand<T> cmd = _undo.Pop();
T output = cmd.Undo(input);
_redo.Push(cmd);
return output;
}
else
{
return input;
}
}
// Redo the operation
public T Redo(T input)
{
if (_redo.Count > 0)
{
ICommand<T> cmd = _redo.Pop();
T output = cmd.Do(input);
_undo.Push(cmd);
return output;
}
else
{
return input;
}
}
}
Finally, call the following command on the client:
static void Main(string[] args)
{
UndoRedoStack<int> intCalulator = new UndoRedoStack<int>();
int count = 0;
count = intCalulator.Do(new AddIntCommand(10), count);
count = intCalulator.Do(new AddIntCommand(20), count);
Console. WriteLine ("value calculated for the first time: {0}", count );
// Perform the Undo operation once
count = intCalulator.Undo(count);
Console. WriteLine ("the second calculation value is {0}", count );
Console.ReadKey();
}
References:
Http://brendan.enrick.com/post/Simple-C-Stack-Implementation
Http://www.cambiaresearch.com/articles/82/generic-undoredo-stack-in-csharp