程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中泛型容器Stack<T>的用法,以及借此實現”撤銷/重做”功能,

C#中泛型容器Stack<T>的用法,以及借此實現”撤銷/重做”功能,

編輯:C#入門知識

C#中泛型容器Stack<T>的用法,以及借此實現”撤銷/重做”功能,


 

.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的泛型實現,提供了若干方法和屬性,比如入棧、出棧、查看棧頂元素,查看棧內集合元素數量,等等。棧的最大特點是先進後出,可以把棧想像成一堆疊起來的盤子,入棧就是把一個個盤子放到最上面,出棧就是從最上面把盤子拿掉。用法比較簡單:

 

    class Program
    {
        static void Main(string[] args)
        {
            var customer1 = new Customer() {ID = 1, Name = "張三", Gender = "男"};
            var customer2 = new Customer() { ID = 2, Name = "李四", Gender = "男" };
            Stack<Customer> stackCustomers = new Stack<Customer>();
            //入棧
            stackCustomers.Push(customer1);
            stackCustomers.Push(customer2);
            //查看棧頂元素
            Customer topCustomer = stackCustomers.Peek();
            Console.WriteLine("棧頂元素是:" + topCustomer.Name);
            //遍歷所有棧內元素
            foreach (var customer in stackCustomers)
            {
                Console.WriteLine("id is {0},name is {1}", customer.ID, customer.Name);
            }
            //出棧
            Customer outCustomer = stackCustomers.Pop();
            Console.WriteLine("正在出棧的是:" + outCustomer.Name);
            Console.WriteLine("當前棧內元素數量為:" + stackCustomers.Count);
            Console.ReadKey();
        }
    }
    public class Customer
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Gender { get; set; }
    }

 

臨摹一個泛型Stack<T> 

 

泛型Stack類內部維護這一個泛型數組和索引指針,且指針的初始位置是-1。

 

入棧就是把指針往前提一位,並把入棧元素賦值給該棧內位置。另外,入棧要考慮是否達到容量上限,如果達到就要給數組擴容。

 

出棧就是讓當前棧位置的元素值為入棧類型的默認值,並大指針後退一位。

 

獲取棧頂元素就是獲取棧當前索引位置對應的元素。

 

    public class MyStack<T>
    {
        //維護T類型的數組
        private T[] _elements;
        protected T[] Elements
        {
            get { return _elements; }
            set { _elements = value; }
        }
        public MyStack()
        {
            _capacity = 5;//初始值
            Elements = new T[Capacity];
        }
        public MyStack(int capacity)
        {
            Capacity = capacity;
            Elements = new T[Capacity];
        }
        //指針
        private int _index = -1;
        public int Index
        {
            get { return _index; }
            set { _index = value; }
        }
        //容量
        private int _capacity;
        public int Capacity
        {
            get { return _capacity; }
            set { _capacity = value; }
        }
        //長度=索引+1
        public int Length
        {
            get { return Index + 1; }
        }
        //入棧
        public void Push(T element)
        {
            if (this.Length == Capacity)
            {
                IncreaseCapacity();
            }
            Index++;
            Elements[Index] = element;
        }
        //出棧
        public T Pop()
        {
            if (this.Length < 1)
            {
                throw new InvalidOperationException("棧內已空");
            }
            T element = Elements[Index];
            //原先位置元素變成默認值
            Elements[Index] = default(T);
            //索引減一
            Index--;
            return element;
        }
        //獲取棧頂元素
        public T Peek()
        {
            if (this.Length < 1)
            {
                throw new InvalidOperationException("棧內已空");
            }
            return Elements[Index];
        }
        private void IncreaseCapacity()
        {
            Capacity++;
            Capacity *= 2;
            //創建新的T類型數組
            T[] newElements = new T[Capacity];
            //把原先的數組復制到新的數組中來
            Array.Copy(Elements, newElements, Elements.Length);
            Elements = newElements;
        }
    }

 

現在,在客戶端,實施一系列的入棧和出棧操作。

 

        static void Main(string[] args)
        {
           //創建泛型Stack實例
            MyStack<int> myStack = new MyStack<int>();
            //遍歷10次入棧
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i + "開始入棧");
                myStack.Push(i);
                Console.WriteLine("當前棧的長度是:" + myStack.Length);
            }
           
            //遍歷10次出棧
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("當前出棧的是" + myStack.Peek());
                myStack.Pop();
                Console.WriteLine("當前棧的長度是:" + myStack.Length);
            }
            //所有出棧結束,再查看棧頂元素拋異常
            try
            {
                myStack.Peek();
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine(ex.Message);
            }
            //所有出棧結束,再出棧拋異常
            try
            {
                myStack.Pop();
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadKey();
        }

 

其實,泛型Stack<T>的內部也是維護著一個數組,數組的容量是動態變化的,這一點很像List<T>,就像這裡提到的。

 

使用泛型Stack<T>實現"撤銷/重做"操作

 

首先,操作或撤銷操作是針對某種類型的撤銷或重做,提煉出一個接口。

 

    public interface ICommand<T>
    {
        T Do(T input);
        T Undo(T input);
    }

 

假設,這裡想實現對整型數的"撤銷/重做"操作。

 

    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;
        }
        //執行操作
        public int Do(int input)
        {
            return input + _value;
        }
        //撤銷操作
        public int Undo(int input)
        {
            return input - _value;
        }
    }

 

接下來,需要一個泛型類來管理所有撤銷或操作命令,把這些命令放在Stack<ICommand<T>>泛型集合中。

 

    //使用泛型Stack實現撤銷或重做
    public class UndoRedoStack<T>
    {
        private Stack<ICommand<T>> _undo;//有關撤銷的泛型stack
        private Stack<ICommand<T>> _redo;//有關重做的泛型stack
        public UndoRedoStack()
        {
            Reset();
        }
        //記錄撤銷的數量
        public int UndoCount
        {
            get { return _undo.Count; }
        }
        //記錄重做的數量
        public int RedoCount
        {
            get { return _redo.Count; }
        }
        //恢復到出廠設置
        public void Reset()
        {
            _undo = new Stack<ICommand<T>>();
            _redo = new Stack<ICommand<T>>();
        }
        //執行操作
        public T Do(ICommand<T> cmd, T input)
        {
            T output = cmd.Do(input);
            //把剛才的命令放入有關撤銷的stack中
            _undo.Push(cmd);
            //一旦啟動一個新命令,有關重做的stack清空
            _redo.Clear();
            return output;
        }
        //撤銷操作
        public T Undo(T input)
        {
            if (_undo.Count > 0)
            {
                //出棧
                ICommand<T> cmd = _undo.Pop();
                T output = cmd.Undo(input);
                _redo.Push(cmd);
                return output;
            }
            else
            {
                return input;
            }
        }
        //重做操作
        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;
            }
        }
    }

 

最後,在客戶端按如下調用:

 

        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("第一次計算的值為:{0}",count);
            //執行撤銷操作一次
            count = intCalulator.Undo(count);
            Console.WriteLine("第二次計算的值為:{0}",count);
            Console.ReadKey();
        }    


 

參考資料:

http://brendan.enrick.com/post/Simple-C-Stack-Implementation

http://www.cambiaresearch.com/articles/82/generic-undoredo-stack-in-csharp

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved