WPF進階之接口(4):ICommand實現詳解


上一章WPF進階之接口(3):INotifyPropertyChanged,ICommand中我們遺留了幾個問題,我將在本節中做出解釋。在詳細解釋ICommand實現之前,我們現在關注一下什么是:弱引用(WeakReference)

弱引用:

表示弱引用,即在引用對象的同時仍然允許垃圾回收來回收該對象。

如果應用程序的代碼可以訪問一個正由該程序使用的對象,垃圾回收器就不能收集該對象,那么,就認為應用程序對該對象具有強引用。

弱引用允許應用程序訪問對象,同時也允許垃圾回收器收集相應的對象。如果不存在強引用,則弱引用的有限期只限於收集對象前的一個不確定的時間段。使用弱引用時,應用程序仍可對該對象進行強引用,這樣做可防止該對象被收集。但始終存在這樣的風險:垃圾回收器在重新建立強引用之前先處理該對象。

弱引用特別適合以下對象:占用大量內存,但通過垃圾回收功能回收以后很容易重新創建。

假設 Windows 窗體應用程序中的一個樹視圖向用戶顯示了復雜的選項層次結構。如果基礎數據量很大,則用戶使用應用程序中的其他部分時,在內存中保留該樹會導致效率低下。

當用戶切換到應用程序的其他部分時,可使用 WeakReference 類來創建對該樹的弱引用,並銷毀所有強引用。當用戶切換回該樹時,應用程序會嘗試獲得對該樹的強引用,如果得到,就不必重新構造該樹。

要對某個對象建立弱引用,請使用要跟蹤的對象的實例創建一個 WeakReference。然后將 Target 屬性設置為該對象,將該對象設置為 null。

可創建短弱引用或長弱引用:

短

垃圾回收功能回收對象后,短弱引用的目標會變為 null。弱引用本身是托管對象,和任何其他托管對象一樣需要經過垃圾回收。短弱引用是 WeakReference 的默認構造函數。

長

調用對象的 Finalize 方法后,會保留長弱引用。這樣,您就可以重新創建該對象,但該對象仍保持不可預知的狀態。要使用長引用,請在 WeakReference 構造函數中指定 true。

如果對象的類型沒有 Finalize 方法,則會應用短弱引用功能,該弱引用只在目標被收集之前有效,運行終結器之后可以隨時收集目標。

要建立強引用並重新使用該對象,請將 WeakReference 的 Target 屬性強制轉換為該對象的類型。如果 Target 屬性返回 null,則表示對象已被收集;否則,您可繼續使用該對象,因為應用程序已重新獲得了對它的強引用。

這是摘自MSDN的解釋,那么簡單來說,弱引用與強引用的區別在於,它允許GC回收。

ICommand實現:

在WPF進階之接口(3):INotifyPropertyChanged,ICommand我們看到在ICommand的實現代碼中:

internal class CommandManagerHelper
    {
        internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                // Take a snapshot of the handlers before we call out to them since the handlers
                // could cause the array to me modified while we are reading it.

                EventHandler[] callees = new EventHandler[handlers.Count];
                int count = 0;

                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler handler = reference.Target as EventHandler;
                    if (handler == null)
                    {
                        // Clean up old handlers that have been collected
                        handlers.RemoveAt(i);
                    }
                    else
                    {
                        callees[count] = handler;
                        count++;
                    }
                }

                // Call the handlers that we snapshotted
                for (int i = 0; i < count; i++)
                {
                    EventHandler handler = callees[i];
                    handler(null, EventArgs.Empty);
                }
            }
        }

        internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                foreach (WeakReference handlerRef in handlers)
                {
                    EventHandler handler = handlerRef.Target as EventHandler;
                    if (handler != null)
                    {
                        CommandManager.RequerySuggested += handler;
                    }
                }
            }
        }

        internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                foreach (WeakReference handlerRef in handlers)
                {
                    EventHandler handler = handlerRef.Target as EventHandler;
                    if (handler != null)
                    {
                        CommandManager.RequerySuggested -= handler;
                    }
                }
            }
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
        {
            AddWeakReferenceHandler(ref handlers, handler, -1);
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
        {
            if (handlers == null)
            {
                handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
            }

            handlers.Add(new WeakReference(handler));
        }

        internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
        {
            if (handlers != null)
            {
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler existingHandler = reference.Target as EventHandler;
                    if ((existingHandler == null) || (existingHandler == handler))
                    {
                        // Clean up old handlers that have been collected
                        // in addition to the handler that is to be removed.
                        handlers.RemoveAt(i);
                    }
                }
            }
        }
    }

CommandManagerHelper類主要是對將事件用WealReference的形式實現,Add和Remove。為什么要這樣處理呢,在C#中的弱事件(Weak Events in C#)我們談到了,雖然C#在實現事件訂閱機制的時候是線程安全的,但在用戶的使用過程中仍然可能導致事件的handler不能同步,即一個線程可能已經將事件注銷了,而另一個線程可能仍然要觸發事件,那時系統就會拋出 NullReferenceException而導致系統崩潰。原因請詳細閱讀C#中的弱事件(Weak Events in C#)一文。

可以看到我們在實現事件的訂閱機制的時候,必須要考慮到上述問題。上一章節ICommand實現正是采用了Solution2(弱引用)來解決此問題。即每次添加、刪除和觸發事件之前,必須檢查WeakReference的List,查看是否已經存在,或是已經被GC回收,相應采取處理。

這樣與MVVM相關的幾個基本的Inerface,我已經陸續向大家介紹完畢。下一節,將向大家詳細介紹控件的重寫,可能在此過程中仍然要涉及幾個接口(IList,IEnuerator,ICollection等),但是這些Interface主要是與控件重寫技術相關的,我將放入相應章節中做出解釋。

轉自:http://hi.baidu.com/leo_han/item/46128d371590ba9db90c030a


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM