[C.C++] C#中泛型容器Stack<T>的用法并实现”撤销/重做”功能

1933 0
Honkers 2022-11-5 09:02:32 | 显示全部楼层 |阅读模式
.Net为我们提供了众多的泛型集合。比如,Stack<T>先进后出,Queue<T>先进先出,List<T>集合元素可排序,支持索引,LinkedList<T>,双向链表的泛型实现,不支持索引;ISet<T>不允许被复制,他有2个实现,一个是HashSet<T>,不维持集合元素的排序,另一个是SortedSet<T>,支持集合元素的排序;IDictionary<TKey, TValue>是一个字典集合的泛型接口,SortedList<TKey,TValue>实现了IDictionary<TKey, TValue>,但同时也是集合,维持集合元素的排序,支持按键或按值索引。
本篇体验Stack<T>的用法。
基本用法

Stack<T>是Stack的泛型实现,提供了若干方法和属性,比如入栈、出栈、查看栈顶元素,查看栈内集合元素数量,等等。栈的最大特点是先进后出,可以把栈想像成一堆叠起来的盘子,入栈就是把一个个盘子放到最上面,出栈就是从最上面把盘子拿掉。用法比较简单:
  1.     class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             var customer1 = new Customer() {ID = 1, Name = "张三", Gender = "男"};
  6.             var customer2 = new Customer() { ID = 2, Name = "李四", Gender = "男" };
  7.             Stack<Customer> stackCustomers = new Stack<Customer>();
  8.             //入栈
  9.             stackCustomers.Push(customer1);
  10.             stackCustomers.Push(customer2);
  11.             //查看栈顶元素
  12.             Customer topCustomer = stackCustomers.Peek();
  13.             Console.WriteLine("栈顶元素是:" + topCustomer.Name);
  14.             //遍历所有栈内元素
  15.             foreach (var customer in stackCustomers)
  16.             {
  17.                 Console.WriteLine("id is {0},name is {1}", customer.ID, customer.Name);
  18.             }
  19.             //出栈
  20.             Customer outCustomer = stackCustomers.Pop();
  21.             Console.WriteLine("正在出栈的是:" + outCustomer.Name);
  22.             Console.WriteLine("当前栈内元素数量为:" + stackCustomers.Count);
  23.             Console.ReadKey();
  24.         }
  25.     }
  26.     public class Customer
  27.     {
  28.         public int ID { get; set; }
  29.         public string Name { get; set; }
  30.         public string Gender { get; set; }
  31.     }
复制代码
临摹一个泛型Stack<T>

泛型Stack类内部维护这一个泛型数组和索引指针,且指针的初始位置是-1。
入栈就是把指针往前提一位,并把入栈元素赋值给该栈内位置。另外,入栈要考虑是否达到容量上限,如果达到就要给数组扩容。
出栈就是让当前栈位置的元素值为入栈类型的默认值,并大指针后退一位。
获取栈顶元素就是获取栈当前索引位置对应的元素。
  1.     public class MyStack<T>
  2.     {
  3.         //维护T类型的数组
  4.         private T[] _elements;
  5.         protected T[] Elements
  6.         {
  7.             get { return _elements; }
  8.             set { _elements = value; }
  9.         }
  10.         public MyStack()
  11.         {
  12.             _capacity = 5;//初始值
  13.             Elements = new T[Capacity];
  14.         }
  15.         public MyStack(int capacity)
  16.         {
  17.             Capacity = capacity;
  18.             Elements = new T[Capacity];
  19.         }
  20.         //指针
  21.         private int _index = -1;
  22.         public int Index
  23.         {
  24.             get { return _index; }
  25.             set { _index = value; }
  26.         }
  27.         //容量
  28.         private int _capacity;
  29.         public int Capacity
  30.         {
  31.             get { return _capacity; }
  32.             set { _capacity = value; }
  33.         }
  34.         //长度=索引+1
  35.         public int Length
  36.         {
  37.             get { return Index + 1; }
  38.         }
  39.         //入栈
  40.         public void Push(T element)
  41.         {
  42.             if (this.Length == Capacity)
  43.             {
  44.                 IncreaseCapacity();
  45.             }
  46.             Index++;
  47.             Elements[Index] = element;
  48.         }
  49.         //出栈
  50.         public T Pop()
  51.         {
  52.             if (this.Length < 1)
  53.             {
  54.                 throw new InvalidOperationException("栈内已空");
  55.             }
  56.             T element = Elements[Index];
  57.             //原先位置元素变成默认值
  58.             Elements[Index] = default(T);
  59.             //索引减一
  60.             Index--;
  61.             return element;
  62.         }
  63.         //获取栈顶元素
  64.         public T Peek()
  65.         {
  66.             if (this.Length < 1)
  67.             {
  68.                 throw new InvalidOperationException("栈内已空");
  69.             }
  70.             return Elements[Index];
  71.         }
  72.         private void IncreaseCapacity()
  73.         {
  74.             Capacity++;
  75.             Capacity *= 2;
  76.             //创建新的T类型数组
  77.             T[] newElements = new T[Capacity];
  78.             //把原先的数组复制到新的数组中来
  79.             Array.Copy(Elements, newElements, Elements.Length);
  80.             Elements = newElements;
  81.         }
  82.     }
复制代码
现在,在客户端,实施一系列的入栈和出栈操作。
  1.         static void Main(string[] args)
  2.         {
  3.            //创建泛型Stack实例
  4.             MyStack<int> myStack = new MyStack<int>();
  5.             //遍历10次入栈
  6.             for (int i = 0; i < 10; i++)
  7.             {
  8.                 Console.WriteLine(i + "开始入栈");
  9.                 myStack.Push(i);
  10.                 Console.WriteLine("当前栈的长度是:" + myStack.Length);
  11.             }
  12.             //遍历10次出栈
  13.             for (int i = 0; i < 10; i++)
  14.             {
  15.                 Console.WriteLine("当前出栈的是" + myStack.Peek());
  16.                 myStack.Pop();
  17.                 Console.WriteLine("当前栈的长度是:" + myStack.Length);
  18.             }
  19.             //所有出栈结束,再查看栈顶元素抛异常
  20.             try
  21.             {
  22.                 myStack.Peek();
  23.             }
  24.             catch (InvalidOperationException ex)
  25.             {
  26.                 Console.WriteLine(ex.Message);
  27.             }
  28.             //所有出栈结束,再出栈抛异常
  29.             try
  30.             {
  31.                 myStack.Pop();
  32.             }
  33.             catch (InvalidOperationException ex)
  34.             {
  35.                 Console.WriteLine(ex.Message);
  36.             }
  37.             Console.ReadKey();
  38.         }
复制代码
其实,泛型Stack<T>的内部也是维护着一个数组,数组的容量是动态变化的,这一点很像List<T>,就像这里提到的。
使用泛型Stack<T>实现"撤销/重做"操作
首先,操作或撤销操作是针对某种类型的撤销或重做,提炼出一个接口。
  1.     public interface ICommand<T>
  2.     {
  3.         T Do(T input);
  4.         T Undo(T input);
  5.     }
复制代码
假设,这里想实现对整型数的"撤销/重做"操作。
  1.     public class AddIntCommand : ICommand<int>
  2.     {
  3.         private int _value;
  4.         public int Value
  5.         {
  6.             get { return _value; }
  7.             set { _value = value; }
  8.         }
  9.         public AddIntCommand()
  10.         {
  11.             _value = 0;
  12.         }
  13.         public AddIntCommand(int value)
  14.         {
  15.             _value = value;
  16.         }
  17.         //执行操作
  18.         public int Do(int input)
  19.         {
  20.             return input + _value;
  21.         }
  22.         //撤销操作
  23.         public int Undo(int input)
  24.         {
  25.             return input - _value;
  26.         }
  27.     }
复制代码
接下来,需要一个泛型类来管理所有撤销或操作命令,把这些命令放在Stack<ICommand<T>>泛型集合中。
  1.     //使用泛型Stack实现撤销或重做
  2.     public class UndoRedoStack<T>
  3.     {
  4.         private Stack<ICommand<T>> _undo;//有关撤销的泛型stack
  5.         private Stack<ICommand<T>> _redo;//有关重做的泛型stack
  6.         public UndoRedoStack()
  7.         {
  8.             Reset();
  9.         }
  10.         //记录撤销的数量
  11.         public int UndoCount
  12.         {
  13.             get { return _undo.Count; }
  14.         }
  15.         //记录重做的数量
  16.         public int RedoCount
  17.         {
  18.             get { return _redo.Count; }
  19.         }
  20.         //恢复到出厂设置
  21.         public void Reset()
  22.         {
  23.             _undo = new Stack<ICommand<T>>();
  24.             _redo = new Stack<ICommand<T>>();
  25.         }
  26.         //执行操作
  27.         public T Do(ICommand<T> cmd, T input)
  28.         {
  29.             T output = cmd.Do(input);
  30.             //把刚才的命令放入有关撤销的stack中
  31.             _undo.Push(cmd);
  32.             //一旦启动一个新命令,有关重做的stack清空
  33.             _redo.Clear();
  34.             return output;
  35.         }
  36.         //撤销操作
  37.         public T Undo(T input)
  38.         {
  39.             if (_undo.Count > 0)
  40.             {
  41.                 //出栈
  42.                 ICommand<T> cmd = _undo.Pop();
  43.                 T output = cmd.Undo(input);
  44.                 _redo.Push(cmd);
  45.                 return output;
  46.             }
  47.             else
  48.             {
  49.                 return input;
  50.             }
  51.         }
  52.         //重做操作
  53.         public T Redo(T input)
  54.         {
  55.             if (_redo.Count > 0)
  56.             {
  57.                 ICommand<T> cmd = _redo.Pop();
  58.                 T output = cmd.Do(input);
  59.                 _undo.Push(cmd);
  60.                 return output;
  61.             }
  62.             else
  63.             {
  64.                 return input;
  65.             }
  66.         }
  67.     }
复制代码
最后,在客户端按如下调用:
  1.         static void Main(string[] args)
  2.         {
  3.             UndoRedoStack<int> intCalulator = new UndoRedoStack<int>();
  4.             int count = 0;
  5.             count = intCalulator.Do(new AddIntCommand(10), count);
  6.             count = intCalulator.Do(new AddIntCommand(20), count);
  7.             Console.WriteLine("第一次计算的值为:{0}",count);
  8.             //执行撤销操作一次
  9.             count = intCalulator.Undo(count);
  10.             Console.WriteLine("第二次计算的值为:{0}",count);
  11.             Console.ReadKey();
  12.         }
复制代码
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对中国红客联盟的支持。如果你想了解更多相关内容请查看下面相关链接
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4008
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行