Freezable.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Base / System / Windows / Freezable.cs / 1 / Freezable.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: The Freezable class (plus the FreezableHelper class) 
//              encompasses all of the Freezable pattern.
// 
//              See spec at [....]/medialayer/Shared%20Documents/Freezables.doc
//
// History:
//  05/01/2003 : [....] - Created 
//  07/11/2003 : [....] - Removed subsequent history.  See SourceDepot.
//  08/05/2005 : [....] - Added context information. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized; 
using System.Collections.Generic;
using System.ComponentModel; 
using System.Runtime.InteropServices; 
using System.Windows.Threading;
 
using MS.Internal;                          // for Invariant
using MS.Internal.WindowsBase;              // FriendAccessAllowed
using MS.Utility;                           // FrugalList
 
namespace System.Windows
{ 
    ///  
    /// The Freezable class encapsulates the Freezable pattern for DOs whose
    /// values can potentially be frozen.  See the Freezable documentation for 
    /// more details.
    /// 
    public abstract class Freezable : DependencyObject, ISealable
    { 

#if DEBUG 
 
        private static int _nextID = 1;
 
        private readonly int DebugID = _nextID++;

#endif
 
        #region Protected Constructors
 
        //----------------------------------------------------- 
        //
        //  Protected constructors 
        //
        //-----------------------------------------------------

        ///  
        /// Construct a mutable Freezable.
        ///  
        protected Freezable() 
        {
            Debug.Assert(!Freezable_Frozen 
                    && !Freezable_HasMultipleInheritanceContexts
                    && !(HasHandlers || HasContextInformation),
                    "Initial state is incorrect");
        } 

        #endregion 
 
        #region Public Methods
 
        //------------------------------------------------------
        //
        //  Public methods
        // 
        //-----------------------------------------------------
 
        ///  
        /// Makes a mutable deep base value clone of this Freezable.
        /// 
        /// Caveat: Frozen default values will still be frozen afterwards
        /// 
        /// A clone of the Freezable.
        public Freezable Clone() 
        {
            ReadPreamble(); 
 
            Freezable clone = CreateInstance();
 
            clone.CloneCore(this);
            Debug_VerifyCloneCommon(/* original = */ this, /* clone = */ clone, /* isDeepClone = */ true);

            return clone; 
        }
 
        ///  
        /// Makes a mutable current value clone of this Freezable.
        /// 
        /// Caveat: Frozen default values will still be frozen afterwards
        /// 
        /// 
        /// Returns a mutable deep copy of this Freezable that represents 
        /// its current state.
        ///  
        public Freezable CloneCurrentValue() 
        {
            ReadPreamble(); 

            Freezable clone = CreateInstance();

            clone.CloneCurrentValueCore(this); 

            // Freezable implementers who override CloneCurrentValueCore must ensure that 
            // on creation the copy is not frozen.  Debug_VerifyCloneCommon checks for this, 
            // among other things.
            Debug_VerifyCloneCommon(/* original = */ this, /* clone = */ clone, /* isDeepClone = */ true); 

            return clone;
        }
 
        /// 
        ///     Semantically equivalent to Freezable.Clone().Freeze() except that 
        ///     GetAsFrozen avoids a copying any portions of the Freezable graph 
        ///     which are already frozen.
        ///  
        public Freezable GetAsFrozen()
        {
            ReadPreamble();
 
            if (IsFrozenInternal)
            { 
                return this; 
            }
 
            Freezable clone = CreateInstance();

            clone.GetAsFrozenCore(this);
            Debug_VerifyCloneCommon(/* original = */ this, /* clone = */ clone, /* isDeepClone = */ false); 

            clone.Freeze(); 
 
            return clone;
        } 


        /// 
        ///     Semantically equivalent to Freezable.CloneCurrentValue().Freeze() except that 
        ///     GetCurrentValueAsFrozen avoids a copying any portions of the Freezable graph
        ///     which are already frozen. 
        ///  
        public Freezable GetCurrentValueAsFrozen()
        { 
            ReadPreamble();

            if (IsFrozenInternal)
            { 
                return this;
            } 
 
            Freezable clone = CreateInstance();
 
            clone.GetCurrentValueAsFrozenCore(this);
            Debug_VerifyCloneCommon(/* original = */ this, /* clone = */ clone, /* isDeepClone = */ false);

            clone.Freeze(); 

            return clone; 
        } 

        ///  
        /// True if this Freezable can be frozen (by calling Freeze())
        /// 
        public bool CanFreeze
        { 
            get
            { 
                return IsFrozenInternal || FreezeCore(/* isChecking = */ true); 
            }
        } 

        /// 
        /// Does an in-place modification to make the object frozen. It is legal to
        /// call this on values that are already frozen. 
        /// 
        /// This exception 
        /// will be thrown if this Freezable can't be frozen. Use 
        /// the CanFreeze property to detect this in advance.
        public void Freeze() 
        {
            // Check up front that the operation will succeed before we begin.
            if (!CanFreeze)
            { 
                throw new InvalidOperationException(SR.Get(SRID.Freezable_CantFreeze));
            } 
 
            Freeze(/* isChecking = */ false);
        } 

        #endregion

        #region Public Properties 

        //------------------------------------------------------ 
        // 
        //  Public Properties
        // 
        //------------------------------------------------------

        /// 
        /// Returns whether or not the Freezable is modifiable.  Attempts 
        /// to set properties on an IsFrozen value result
        /// in exceptions being raised. 
        ///  
        public bool IsFrozen
        { 
            get
            {
                ReadPreamble();
 
                return IsFrozenInternal;
            } 
        } 

        internal bool IsFrozenInternal 
        {
            get
            {
                return Freezable_Frozen; 
            }
        } 
 
        #endregion
        #region Public Events 

        //-----------------------------------------------------
        //
        //  Public Events 
        //
        //------------------------------------------------------ 
 
        /// 
        /// The Changed event is raised whenever something on this 
        /// Freezable is modified.  Note that it is illegal to
        /// add or remove event handlers from a value with
        /// IsFrozen.
        ///  
        /// 
        /// An attempt was made to modify the Changed handler of 
        /// a value with IsFrozen == true. 
        /// 
        public event EventHandler Changed 
        {
            add
            {
                WritePreamble(); 

                if (value != null) 
                { 
                    ChangedInternal += value;
                } 

            }
            remove
            { 
                WritePreamble();
 
                if (value != null) 
                {
                    ChangedInternal -= value; 
                }
            }
        }
 
        internal event EventHandler ChangedInternal
        { 
            add 
            {
                HandlerAdd(value); 

                // Adding/Removing Changed handlers does not raise the Changed event.
                // Therefore we intentionally do not call WritePostscript().
            } 

            remove 
            { 
                HandlerRemove(value);
 
                // Adding/Removing Changed handlers does not raise the Changed event.
                // Therefore we intentionally do not call WritePostscript().
            }
        } 
        #endregion
 
        #region Protected Methods 

        //----------------------------------------------------- 
        //
        //  Protected methods
        //
        //----------------------------------------------------- 

        ///  
        /// Override OnPropertyChanged so that we can fire the Freezable's Changed 
        /// handler in response to a DP changing.
        ///  
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
 
            // The property system will call us back when a SetValue is performed
            // on a Freezable.  The Freezable then walks it's contexts and causes 
            // a subproperty invalidation on each context and fires any changed 
            // handlers that have been registered.
 
            if (!e.IsASubPropertyChange)
            {
                WritePostscript();
            } 

            // OnPropertyChanged is called after the old inheritance context is 
            // removed, but before the new one is added. 
            Debug_DetectContextLeaks();
        } 


        /// 
        /// Create a default instance of a Freezable object. Actual allocation 
        /// will occur in CreateInstanceCore.
        ///  
        /// A new instance of the class 
        protected Freezable CreateInstance()
        { 
            Freezable newFreezable = CreateInstanceCore();

            Debug_VerifyInstance("CreateInstance", this, newFreezable);
 
            return newFreezable;
        } 
 
        //
        ///  
        /// Subclasses must implement this to create instances of themselves.
        /// See the Freezable documentation for examples.
        /// 
        /// A new instance of the class 
        protected abstract Freezable CreateInstanceCore();
 
        ///  
        /// If you derive from Freezable you may need to override this method. Reasons
        /// to override include: 
        ///    1) Your subclass has data that is not exposed via DPs
        ///    2) Your subclass has to perform extra work during construction. For
        ///       example, your subclass implements ISupportInitialize.
        /// 
        /// The default implementation makes deep clones of all writable, locally set
        /// properties including expressions. The property's base value is copied -- not the 
        /// current value. It skips read only DPs. 
        ///
        /// If you do override this method, you MUST call the base implementation. 
        ///
        /// This is called by Clone().
        /// 
        /// The Freezable to clone information from 
        protected virtual void CloneCore(Freezable sourceFreezable)
        { 
            CloneCoreCommon(sourceFreezable, 
                /* useCurrentValue = */ false,
                /* cloneFrozenValues = */ true); 
        }

        /// 
        /// If you derive from Freezable you may need to override this method. Reasons 
        /// to override include:
        ///    1) Your subclass has data that is not exposed via DPs 
        ///    2) Your subclass has to perform extra work during construction. For 
        ///       example, your subclass implements ISupportInitialize.
        /// 
        /// The default implementation goes through all DPs making copies of their
        /// current values. It skips read only and default DPs
        ///
        /// If you do override this method, you MUST call the base implementation. 
        ///
        /// This is called by CloneCurrentValue(). 
        ///  
        /// The Freezable to copy info from
        protected virtual void CloneCurrentValueCore(Freezable sourceFreezable) 
        {
            CloneCoreCommon(sourceFreezable,
                /* useCurrentValue = */ true,
                /* cloneFrozenValues = */ true); 
        }
 
        ///  
        /// If you derive from Freezable you may need to override this method. Reasons
        /// to override include: 
        ///    1) Your subclass has data that is not exposed via DPs
        ///    2) Your subclass has to perform extra work during construction. For
        ///       example, your subclass implements ISupportInitialize.
        /// 
        /// The default implementation makes clones of all writable, unfrozen, locally set
        /// properties including expressions. The property's base value is copied -- not the 
        /// current value. It skips read only DPs and any values which are already frozen. 
        ///
        /// If you do override this method, you MUST call the base implementation. 
        ///
        /// You do not need to Freeze values as they are copied.  The result will be
        /// frozen by GetAsFrozen() before being returned.
        /// 
        /// This is called by GetAsFrozen().
        ///  
        /// The Freezable to clone information from 
        protected virtual void GetAsFrozenCore(Freezable sourceFreezable)
        { 
            CloneCoreCommon(sourceFreezable,
                /* useCurrentValue = */ false,
                /* cloneFrozenValues = */ false);
        } 

        ///  
        /// If you derive from Freezable you may need to override this method. Reasons 
        /// to override include:
        ///    1) Your subclass has data that is not exposed via DPs 
        ///    2) Your subclass has to perform extra work during construction. For
        ///       example, your subclass implements ISupportInitialize.
        ///
        /// The default implementation goes through all DPs making copies of their 
        /// current values. It skips read only DPs and any values which are already frozen.
        /// 
        /// If you do override this method, you MUST call the base implementation. 
        ///
        /// You do not need to Freeze values as they are copied.  The result will be 
        /// frozen by GetCurrentValueAsFrozen() before being returned.
        ///
        /// This is called by GetCurrentValueAsFrozen().
        ///  
        /// The Freezable to clone information from
        protected virtual void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) 
        { 
            CloneCoreCommon(sourceFreezable,
                /* useCurrentValue = */ true, 
                /* cloneFrozenValues = */ false);
        }

        ///  
        /// If you derive from Freezable you will need to override this if your subclass
        /// has data that is not exposed via DPs. 
        /// 
        /// The default implementation goes through all DPs and returns false
        /// if any DP has an expression or if any Freezable DP cannot freeze. 
        ///
        /// If you do override this method, you MUST call the base implementation.
        ///
        /// This is called by Freeze(). 
        /// 
        /// If this is true, the method will just check 
        /// to see that the object can be frozen, but won't actually freeze it. 
        /// 
        /// True if the Freezable is or can be frozen. 
        protected virtual bool FreezeCore(bool isChecking)
        {
            EffectiveValueEntry[] effectiveValues = EffectiveValues;
            uint numEffectiveValues = EffectiveValuesCount; 

            // Loop through all DPs and call Freeze() on any with a Freezable value. 
            // This loop also checks if the value in any of the DPs prevent this 
            // Freezable from being frozen. If so we return false immediately.
            for (uint i = 0; i < numEffectiveValues; i++) 
            {
                DependencyProperty dp =
                    DependencyProperty.RegisteredPropertyList.List[effectiveValues[i].PropertyIndex];
 
                if (dp != null)
                { 
                    EntryIndex entryIndex = new EntryIndex(i); 
                    // The expression check only needs to be done when isChecking is true
                    // because if we return false here the Freeze() call will fail. 
                    if (isChecking)
                    {
                        if (HasExpression(entryIndex, dp))
                        { 
                            if (TraceFreezable.IsEnabled)
                            { 
                                TraceFreezable.Trace( 
                                    TraceEventType.Warning,
                                    TraceFreezable.UnableToFreezeExpression, 
                                    this,
                                    dp,
                                    dp.OwnerType);
                            } 

                            return false; 
                        } 
                    }
 
                    if (!dp.IsValueType)
                    {
                        object value =
                            GetValueEntry( 
                                entryIndex,
                                dp, 
                                null, 
                                RequestFlags.FullyResolved).Value;
 
                        if (value != null)
                        {
                            Freezable valueAsFreezable = value as Freezable;
 
                            if (valueAsFreezable != null)
                            { 
                                if (!valueAsFreezable.Freeze(isChecking)) 
                                {
                                    if (TraceFreezable.IsEnabled) 
                                    {
                                        TraceFreezable.Trace(
                                            TraceEventType.Warning,
                                            TraceFreezable.UnableToFreezeFreezableSubProperty, 
                                            this,
                                            dp, 
                                            dp.OwnerType); 
                                    }
 
                                    return false;
                                }
                            }
                            else  // not a Freezable 
                            {
                                DispatcherObject valueAsDispatcherObject = value as DispatcherObject; 
 
                                if (valueAsDispatcherObject != null)
                                { 
                                    if (valueAsDispatcherObject.Dispatcher == null)
                                    {
                                        // The property is a free-threaded DispatcherObject; since it's
                                        // already free-threaded it doesn't prevent this Freezable from 
                                        // becoming free-threaded too.
                                        // It is up to the creator of this type to ensure that the 
                                        // DispatcherObject is actually immutable 
                                    }
                                    else 
                                    {
                                        // The value of this property derives from DispatcherObject and
                                        // has thread affinity; return false.
 
                                        if (TraceFreezable.IsEnabled)
                                        { 
                                            TraceFreezable.Trace( 
                                                TraceEventType.Warning,
                                                TraceFreezable.UnableToFreezeDispatcherObjectWithThreadAffinity, 
                                                this,
                                                dp,
                                                dp.OwnerType,
                                                valueAsDispatcherObject ); 
                                        }
 
                                        return false; 
                                    }
                                } 

                                // The property isn't a DispatcherObject.  It may be immutable (such as a string)
                                // or the user may have made it thread-safe.  It's up to the creator of the type to
                                // do the right thing; we return true as an extensibility point. 
                            }
                        } 
                    } 
                }
            } 

            return true;
        }
 
        //
        // _eventStorage is used as a performance/memory speedup when firing change handlers. 
        // It exists once per thread for thread safety, and is used to store the list of change 
        // handlers that are gathered by GetChangeHandlersAndInvalidateSubProperties.  Reusing the
        // same EventStorage gives gains because it doesn't need to be reallocated each time 
        // FireChanged occurs.
        //
        [ThreadStatic]
        static private EventStorage _eventStorage = null; 

        ///  
        /// Property to access and intialize the thread static _eventStorage variable. 
        /// 
        private EventStorage CachedEventStorage 
        {
            get
            {
                // make sure _eventStorage is not null - with ThreadStatic it appears that the second 
                // thread to access the variable will set this to null
                if (_eventStorage == null) 
                { 
                    _eventStorage = new EventStorage(INITIAL_EVENTSTORAGE_SIZE);
                } 

                return _eventStorage;
            }
        } 

        ///  
        /// Gets an EventStorage object to be used to cache event handlers and sets it to be 
        /// in use.
        ///  
        /// 
        /// An EventStorage object to be used to cache event handlers that is set
        /// to be in use.
        ///  
        private EventStorage GetEventStorage()
        { 
            EventStorage eventStorage = CachedEventStorage; 

            // if we reach a case where EventStorage is being used - meaning FireChanged called 
            // a handler that in turn called FireChanged which is probably a bad thing to have
            // happen - just allocate a new one that won't be cached.
            if (eventStorage.InUse)
            { 
                // use the cached EventStorage's physical size as an estimate of how big we
                // need to be in order to avoid growing the newly created EventStorage 
                int cachedPhysicalSize = eventStorage.PhysicalSize; 
                eventStorage = new EventStorage(cachedPhysicalSize);
            } 

            eventStorage.InUse = true;

            return eventStorage; 
        }
 
        ///  
        /// This method is called when a modification happens to the Freezable object.
        ///  
        protected virtual void OnChanged()
        {
        }
 

        ///  
        /// This method walks up the context graph recursively, gathering all change handlers that 
        /// exist at or above the current node, placing them in calledHandlers.  While
        /// performing the walk it will also call OnChanged and InvalidateSubProperty on all 
        /// DO/DP pairs encountered on the walk.
        /// 
        private void GetChangeHandlersAndInvalidateSubProperties(ref EventStorage calledHandlers)
        { 
            this.OnChanged();
 
            Freezable contextAsFreezable; 

            if (Freezable_UsingSingletonContext) 
            {
                DependencyObject context = SingletonContext;

                contextAsFreezable = context as Freezable; 
                if (contextAsFreezable != null)
                { 
                    contextAsFreezable.GetChangeHandlersAndInvalidateSubProperties(ref calledHandlers); 
                }
 
                if (SingletonContextProperty != null)
                {
                    context.InvalidateSubProperty(SingletonContextProperty);
                } 
            }
            else if (Freezable_UsingContextList) 
            { 
                FrugalObjectList contextList = ContextList;
 
                DependencyObject lastDO = null;

                int deadRefs = 0;
                for (int i = 0, count = contextList.Count; i < count; i++) 
                {
                    FreezableContextPair currentContext = contextList[i]; 
 
                    DependencyObject currentDO = (DependencyObject)currentContext.Owner.Target;
                    if (currentDO != null) 
                    {
                        // we only want to grab change handlers once per context reference - so skip
                        // until we find a new one
                        if (currentDO != lastDO) 
                        {
                            contextAsFreezable = currentDO as Freezable; 
                            if (contextAsFreezable != null) 
                            {
                                contextAsFreezable.GetChangeHandlersAndInvalidateSubProperties(ref calledHandlers); 
                            }

                            lastDO = currentDO;
                        } 

                        if (currentContext.Property != null) 
                        { 
                            currentDO.InvalidateSubProperty(currentContext.Property);
                        } 
                    }
                    else
                    {
                        ++deadRefs; 
                    }
                } 
 
                PruneContexts(contextList, deadRefs);
            } 


            GetHandlers(ref calledHandlers);
        } 

 
        ///  
        /// Extenders of Freezable must call this method at the beginning of any
        /// public API which reads the state of the object.  (e.g., a proprety getter.) 
        /// This ensures that the object is being accessed from a valid thread.
        /// 
        protected void ReadPreamble()
        { 
            VerifyAccess();
        } 
 
        /// 
        /// Extenders of Freezable must call this method prior to changing the state 
        /// of the object (e.g. the beginning of a property setter.)  This ensures that
        /// the object is not frozen and is being accessed from a valid thread.
        /// 
        protected void WritePreamble() 
        {
            VerifyAccess(); 
 
            if (IsFrozenInternal)
            { 
                throw new InvalidOperationException(
                    SR.Get(SRID.Freezable_CantBeFrozen,GetType().FullName));
            }
        } 

        ///  
        /// Extenders of Freezable must call this method at the end of an API which 
        /// changed the state of the object (e.g., at the end of a property setter) to
        /// raise the Changed event.  Multiple state changes within a method or 
        /// property may be "batched" into a single call to WritePostscript().
        /// 
        protected void WritePostscript()
        { 
            FireChanged();
        } 
 
        /// 
        /// Extenders of Freezable call this to set in a new value for internal 
        /// properties or other embedded values that themselves are DependencyObjects.
        /// This method insures that the appropriate context pointers are set up for
        ///  the old and the new Dependency objects.
        /// 
        /// In this version the property is set to be null since
        /// it is not explicitly specified. 
        /// 
        /// 
        /// The previous value of the property. 
        /// The new value to set into the property
        protected void OnFreezablePropertyChanged(
            DependencyObject oldValue,
            DependencyObject newValue 
            )
        { 
            OnFreezablePropertyChanged(oldValue, newValue, null); 
        }
 
        /// 
        /// Extenders of Freezable call this to set in a new value for internal
        /// properties or other embedded values that themselves are DependencyObjects.
        /// This method insures that the appropriate context pointers are set up for 
        /// the old and the new DependencyObject objects.
        ///  
        /// The previous value of the property. 
        /// The new value to set into the property
        /// The property that is being changed or null if none 
        protected void OnFreezablePropertyChanged(
            DependencyObject oldValue,
            DependencyObject newValue,
            DependencyProperty property 
            )
        { 
            // 

 



            if (newValue != null) 
            {
                EnsureConsistentDispatchers(this, newValue); 
            } 

            if (oldValue != null) 
            {
                RemoveSelfAsInheritanceContext(oldValue, property);
            }
 
            if (newValue != null)
            { 
                ProvideSelfAsInheritanceContext(newValue, property); 
            }
        } 

        /// 
        /// Helper method that just invokes Freeze on provided
        /// Freezable if it's not null.  Otherwise it doesn't do anything. 
        /// 
        /// Freezable to freeze. 
        /// If this is true, the method will just check 
        /// to see that the object can be frozen, but won't actually freeze it.
        ///  
        /// True if the Freezable was or can be frozen.
        /// False if isChecking was true and the Freezable can't be frozen.
        /// 
        /// This exception 
        /// will be thrown if isChecking is passed in as false and this
        /// Freezable can't be frozen. 
        // 

        static protected internal bool Freeze(Freezable freezable, bool isChecking) 
        {
            if (freezable != null)
            {
                return freezable.Freeze(isChecking); 
            }
 
            // <[....]> I guess something that's null is always frozen. 
            return true;
        } 

        #endregion  // Protected Methods

        #region ISealable 

        ///  
        /// Can this freezable be sealed 
        /// 
        bool ISealable.CanSeal 
        {
            get { return CanFreeze; }
        }
 
        /// 
        /// Is this freezable sealed 
        ///  
        bool ISealable.IsSealed
        { 
            get { return IsFrozen; }
        }

        ///  
        /// Seal this freezable
        ///  
        void ISealable.Seal() 
        {
            Freeze(); 
        }

        #endregion ISealable
 
        #region Internal Methods
 
        ///  
        /// Clears off the context storage and all Changed event handlers
        ///  
        internal void ClearContextAndHandlers()
        {
            Freezable_UsingHandlerList = false;
            Freezable_UsingContextList = false; 
            Freezable_UsingSingletonHandler = false;
            Freezable_UsingSingletonContext = false; 
            _contextStorage = null; 
            _property = null;
        } 


        /// 
        /// Raises changed notifications for this Freezable.  This includes 
        /// calling the OnChanged virtual, invalidating sub properties, and
        /// raising the Changed event. 
        ///  
        internal void FireChanged()
        { 
            // to avoid access costs, we start with calledHandlers at null and then
            // set it the first time we encounter change handlers that need to be stored.
            EventStorage calledHandlers = null;
 
            GetChangeHandlersAndInvalidateSubProperties(ref calledHandlers);
 
            // Fire all of the change handlers 
            if (calledHandlers != null)
            { 
                for (int i = 0, count = calledHandlers.Count; i < count; i++)
                {
                    // Note: there is a known issue here where if one of these handlers
                    // throws an exception, then we effectively will no longer be able to 
                    // use the EventStorage cache since it will not be possible to set its InUse flag
                    // to false, and we will also keep any memory it was pointing to alive. 
                    // Everything will continue to function normally, however, we will be allocating 
                    // a new EventStorage each time rather than using the one stored in the cache.
                    // Catching the exception and clearing the flag (and nulling 
                    // out the contents) will solve it, but due to Task #45099 on the exception
                    // strategy for the property engine, this has not yet been implemented.
                    //
                    // call the function and then set to null to avoid hanging on to any 
                    // references.
                    calledHandlers[i](this, EventArgs.Empty); 
                    calledHandlers[i] = null; 
                }
 
                // we no longer need the EventStorage object - clear its contents and set
                // it to not be in use.
                calledHandlers.Clear();
                calledHandlers.InUse = false; 
            }
        } 
 
        /// 
        /// Calling DependencyObject.Seal() on a Freezable will leave it in a weird 
        /// state - it won't be free-threaded, but since Seal and Freeze use the
        /// same bit, the Freezable will think it is Frozen.  We therefore disallow
        /// calling Seal() on a Freezable.
        ///  
        internal override void Seal()
        { 
            Invariant.Assert(false); 
        }
 
        #endregion  // Internal Methods

        #region Private methods
 
        private bool Freeze(bool isChecking)
        { 
            if (isChecking) 
            {
                ReadPreamble(); 

                return FreezeCore(true);
            }
            else if (!IsFrozenInternal) 
            {
                WritePreamble(); 
 
                // Check with derived classes to see how they feel about this.
                // If our caller didn't check CanFreeze this may throw 
                // an exception.
                FreezeCore(false);

                // Any cached default values created using the FreezableDefaultValueFactory 
                // must be removed and frozen. Leaving them alone is not an option since they will
                // attempt to promote themselves to locally-set if the user modifies them - 
                // at that point this object will be sealed and the SetValue call will throw an 
                // exception. For Freezables we're required to freeze all DPs, so for performance
                // we simply toss out the cache and return the frozen default prototype, which has 
                // exactly the same state as the cached default (see PropertyMetadata.GetDefaultValue()).
                PropertyMetadata.RemoveAllCachedDefaultValues(this);

                // Since this object no longer changes it won't be able to notify dependents 
                DependentListMapField.ClearValue(this);
 
                // The heart of Freeze.  IsFrozen will now return 
                // true, we keep the handler status bits since we haven't changed our
                // handler storage yet. 
                Freezable_Frozen = true;

                this.DetachFromDispatcher();
 
                // We do notify now, since we're "changing" to frozen.  But not
                // until after everything below us is frozen. 
                FireChanged(); 

                // Clear off event handler/context flags when becoming frozen.  We don't need to call 
                // OnInheritanceContextChanged because a Frozen freezable has no one listening to its
                // InheritanceContextChanged event. Listeners are added when either a BindingExpression
                // or ResourceReferenceExpression is set into a DP. Both derive from Expression, and
                // calling Freeze on any Freezable with a DP set to an Expression will throw an exception. 
                Debug_AssertNoInheritanceContextListeners();
                ClearContextAndHandlers(); 
 
                WritePostscript();
            } 

            return true;
        }
 
        // Makes a deep clone of a Freezable.  Helper method for
        // CloneCore(), CloneCurrentValueCore() and GetAsFrozenCore() 
        // 
        // If useCurrentValue is true it calls GetValue on each of the sourceFreezable's DPs; if false
        // it uses ReadLocalValue. 
        private void CloneCoreCommon(Freezable sourceFreezable, bool useCurrentValue, bool cloneFrozenValues)
        {
            EffectiveValueEntry[] srcEffectiveValues = sourceFreezable.EffectiveValues;
            uint srcEffectiveValueCount = sourceFreezable.EffectiveValuesCount; 

            // Iterate through the effective values array.  Note that default values aren't 
            // stored here so the only defaults we'll come across are modified defaults, 
            // which useCurrentValue = true uses and useCurrentValue = false ignores.
            for (uint i = 0; i < srcEffectiveValueCount; i++) 
            {
                EffectiveValueEntry srcEntry = srcEffectiveValues[i];

                DependencyProperty dp = DependencyProperty.RegisteredPropertyList.List[srcEntry.PropertyIndex]; 

                // We need to skip ReadOnly properties otherwise SetValue will fail 
                if ((dp != null) && !dp.ReadOnly) 
                {
                    object sourceValue; 

                    EntryIndex entryIndex = new EntryIndex(i);

                    if (useCurrentValue) 
                    {
                        // Default values aren't in the EffectiveValues array 
                        // so we won't see them as we iterate.  We do copy modified defaults. 
                        Debug.Assert(srcEntry.BaseValueSourceInternal != BaseValueSourceInternal.Default || srcEntry.HasModifiers);
 
                        sourceValue = sourceFreezable.GetValueEntry(
                                            entryIndex,
                                            dp,
                                            null, 
                                            RequestFlags.FullyResolved).Value;
 
                        // GetValue should not have returned UnsetValue 
                        Debug.Assert(sourceValue != DependencyProperty.UnsetValue);
                    } 
                    else // use base values
                    {
                        // If the local value has modifiers, ReadLocalValue will return the base
                        // value, which is what we want.  A modified default will return UnsetValue, 
                        // which will be ignored at the call to SetValue
                        sourceValue = sourceFreezable.ReadLocalValueEntry(entryIndex, dp, true /* allowDeferredReferences */); 
 
                        // For the useCurrentValue = false case we ignore any UnsetValues.
                        if (sourceValue == DependencyProperty.UnsetValue) 
                        {
                            continue;
                        }
 
                        // If the DP is an expression ReadLocalValue will return the actual expression.
                        // In this case we need to copy it. 
                        if (srcEntry.IsExpression) 
                        {
                            sourceValue = ((Expression)sourceValue).Copy(this, dp); 
                        }
                    }

                    // 
                    // If the value of the current DP is a Freezable
                    // we need to recurse and call the appropriate Clone method in 
                    // order to do a deep copy. 
                    //
 
                    Debug.Assert(!(sourceValue is Expression && sourceValue is Freezable),
                        "This logic assumes Expressions and Freezables don't co-derive");

                    Freezable valueAsFreezable = sourceValue as Freezable; 

                    if (valueAsFreezable != null) 
                    { 
                        Freezable valueAsFreezableClone;
 
                        //
                        // Choose between the four possible ways of
                        // cloning a Freezable
                        // 
                        if (cloneFrozenValues) //CloneCore and CloneCurrentValueCore
                        { 
                            valueAsFreezableClone = valueAsFreezable.CreateInstanceCore(); 

                            if (useCurrentValue) 
                            {
                                // CloneCurrentValueCore implementation.  We clone even if the
                                // Freezable is frozen by recursing into CloneCurrentValueCore.
                                valueAsFreezableClone.CloneCurrentValueCore(valueAsFreezable); 
                            }
                            else 
                            { 
                                // CloneCore implementation.  We clone even if the Freezable is
                                // frozen by recursing into CloneCore. 
                                valueAsFreezableClone.CloneCore(valueAsFreezable);
                            }

                            sourceValue = valueAsFreezableClone; 
                            Debug_VerifyCloneCommon(valueAsFreezable, valueAsFreezableClone, /*isDeepClone=*/ true);
                        } 
                        else // skip cloning frozen values 
                        {
 
                            if (!valueAsFreezable.IsFrozen)
                            {
                                valueAsFreezableClone = valueAsFreezable.CreateInstanceCore();
 
                                if (useCurrentValue)
                                { 
                                    // GetCurrentValueAsFrozenCore implementation.  Only clone if the 
                                    // Freezable is mutable by recursing into GetCurrentValueAsFrozenCore.
                                    valueAsFreezableClone.GetCurrentValueAsFrozenCore(valueAsFreezable); 
                                }
                                else
                                {
                                    // GetAsFrozenCore implementation.  Only clone if the Freezable is 
                                    // mutable by recursing into GetAsFrozenCore.
                                    valueAsFreezableClone.GetAsFrozenCore(valueAsFreezable); 
                                } 

                                sourceValue = valueAsFreezableClone; 
                                Debug_VerifyCloneCommon(valueAsFreezable, valueAsFreezableClone, /*isDeepClone=*/ false);
                            }
                        }
                    } 

                    SetValue(dp, sourceValue); 
                } 
            }
        } 

        // Throws if owner/child are not context free and on different dispatchers.
        private static void EnsureConsistentDispatchers(DependencyObject owner, DependencyObject child)
        { 
            Debug.Assert(owner != null && child != null,
                "Caller should guard against passing null owner/child."); 
 
            // It is illegal to set a DependencyObject from one Dispatcher into a owner
            // being serviced by a different Dispatcher (i.e., they need to be on 
            // the same thread or be context free (Dispatcher == null))
            if (owner.Dispatcher != null &&
                child.Dispatcher != null &&
                owner.Dispatcher != child.Dispatcher) 
            {
                throw new InvalidOperationException( 
                    SR.Get(SRID.Freezable_AttemptToUseInnerValueWithDifferentThread)); 
            }
        } 

        // These methods provide an abstraction for managing Freezable context
        // information - the context information being DO/DP pairs that the Freezable maps to.
        // 
        // The methods will attempt to use as little memory as possible to store this information.
        // When there is only one context it will store the information directly, otherwise it will 
        // place it within a list.  When using a list, these methods place the DO/DP pairs so 
        // that DOs are grouped together.  This is done so that when walking the graph, it
        // is easier to track which DO's change handlers have already been gathered. 
        //

        /// 
        /// Removes the context information for a Freezable. 
        /// The DependencyObject to remove that references this Freezable.
        /// The property of the DependencyObject this object maps to or null if none. 
        ///  
        private void RemoveContextInformation(DependencyObject context, DependencyProperty property)
        { 
            Debug.Assert(context != null);

            bool failed = true;
 
            if (Freezable_UsingSingletonContext)
            { 
                if (SingletonContext == context && SingletonContextProperty == property) 
                {
                    RemoveSingletonContext(); 
                    failed = false;
                }
            }
            else if (Freezable_UsingContextList) 
            {
                FrugalObjectList list = ContextList; 
 
                int deadRefs = 0;
                int index = -1; 
                int count = list.Count;

                for (int i = 0; i < count; i++)
                { 
                    FreezableContextPair entry = list[i];
 
                    object owner = entry.Owner.Target; 
                    if (owner != null)
                    { 
                        if (failed && entry.Property == property && owner == context)
                        {
                            index = i;
                            failed = false; 
                        }
                    } 
                    else 
                    {
                        ++deadRefs; 
                    }
                }

                if (index != -1) 
                {
                    Debug.Assert(!failed); 
 
                    list.RemoveAt(index);
                } 

                PruneContexts(list, deadRefs);
            }
 
            // Make sure we actually removed something - if not throw an exception
            if (failed) 
            { 
                throw new ArgumentException(SR.Get(SRID.Freezable_NotAContext), "context");
            } 
        }

        /// 
        /// Removes the single piece of contextual information that we have and updates all flags 
        /// accordingly.
        ///  
        private void RemoveSingletonContext() 
        {
            Debug.Assert(Freezable_UsingSingletonContext); 
            Debug.Assert(SingletonContext != null);

            if (HasHandlers)
            { 
                _contextStorage = ((HandlerContextStorage)_contextStorage)._handlerStorage;
            } 
            else 
            {
                _contextStorage = null; 
            }

            Freezable_UsingSingletonContext = false;
        } 

        ///  
        /// Removes the context list and updates all flags accordingly. 
        /// 
        private void RemoveContextList() 
        {
            Debug.Assert(Freezable_UsingContextList);

            if (HasHandlers) 
            {
                _contextStorage = ((HandlerContextStorage)_contextStorage)._handlerStorage; 
            } 
            else
            { 
                _contextStorage = null;
            }

            Freezable_UsingContextList = false; 
        }
 
 
        /// 
        /// Helper function to add context information to a Freezable. 
        /// 
        /// The DependencyObject to add that references this Freezable.
        /// The property of the DependencyObject this object maps to or null if none.
        internal override void AddInheritanceContext(DependencyObject context, DependencyProperty property) 
        {
            Debug.Assert(context != null); 
 
            //
 


            if (!IsFrozenInternal)
            { 
                DependencyObject oldInheritanceContext = InheritanceContext;
 
                AddContextInformation(context, property); 

                // Check if the context has changed 
                // If we are frozen, or we already had multiple contexts, the context has not changed
                if (oldInheritanceContext != InheritanceContext)
                {
                    OnInheritanceContextChanged(EventArgs.Empty); 
                }
            } 
        } 

        ///  
        /// Helper function to remove context information from a Freezable.
        /// 
        /// The DependencyObject that references this Freezable.
        /// The property of the DependencyObject this object maps to or null if none. 
        internal override void RemoveInheritanceContext(DependencyObject context, DependencyProperty property)
        { 
            Debug.Assert(context != null); 

            if (!IsFrozenInternal) 
            {
                DependencyObject oldInheritanceContext = InheritanceContext;

                RemoveContextInformation(context, property); 

                // Check if the context has changed 
                // If we are frozen, or we already had multiple contexts, the context has not changed 
                if (oldInheritanceContext != InheritanceContext)
                { 
                    OnInheritanceContextChanged(EventArgs.Empty);
                }
            }
        } 

        ///  
        /// Adds context information to a Freezable. 
        /// The DependencyObject to add that references this Freezable.
        /// The property of the DependencyObject this object maps to or null if none. 
        /// 
        internal void AddContextInformation(DependencyObject context, DependencyProperty property)
        {
            Debug.Assert(context != null); 

            if (Freezable_UsingSingletonContext) 
            { 
                ConvertToContextList();
            } 

            if (Freezable_UsingContextList)
            {
                AddContextToList(context, property); 
            }
            else 
            { 
                AddSingletonContext(context, property);
            } 
        }

        /// 
        /// Helper function to convert to using a list to store context information. 
        /// The SingletonContext is inserted into the list.
        ///  
        private void ConvertToContextList() 
        {
            Debug.Assert(Freezable_UsingSingletonContext); 

            // The list is initialized with capacity for 2 entries since we
            // know we have a 2nd context to insert, hence the conversion
            // from the singleton context state. 
            FrugalObjectList list = new FrugalObjectList(2);
 
            // Note: This converts the SingletonContext from a strong reference to a WeakReference 
            list.Add(new FreezableContextPair(SingletonContext, SingletonContextProperty));
 
            if (HasHandlers)
            {
                ((HandlerContextStorage)_contextStorage)._contextStorage = list;
            } 
            else
            { 
                _contextStorage = list; 
            }
 
            Freezable_UsingContextList = true;
            Freezable_UsingSingletonContext = false;

            // clear the singleton context property 
            _property = null;
        } 
 
        /// 
        /// Helper function to add a singleton context to the Freezable's storage 
        /// The DependencyObject to add that references this Freezable.
        /// The property of the DependencyObject this object maps to or null if none.
        /// 
        private void AddSingletonContext(DependencyObject context, DependencyProperty property) 
        {
            Debug.Assert(!Freezable_UsingSingletonContext && !Freezable_UsingContextList); 
            Debug.Assert(context != null); 

            if (HasHandlers) 
            {
                HandlerContextStorage hps = new HandlerContextStorage();

                hps._handlerStorage = _contextStorage; 
                hps._contextStorage = context;
 
                _contextStorage = hps; 
            }
            else 
            {
                _contextStorage = context;
            }
 
            // set the singleton context property
            _property = property; 
 
            Freezable_UsingSingletonContext = true;
        } 

        /// 
        /// Adds the context information to the context list.  It does this by inserting the
        /// new context information in a location so that all context information referring 
        /// to the same DO are grouped together.
        ///  
        /// The DependencyObject to add that references this Freezable. 
        /// The property of the DependencyObject this object maps to or null if none.
        private void AddContextToList(DependencyObject context, DependencyProperty property) 
        {
            Debug.Assert(context != null);

            FrugalObjectList list = ContextList; 
            int count = list.Count;
            int insertIndx = count;        // insert at the end by default 
            int deadRefs = 0; 

            DependencyObject lastContext = null; 
            bool multipleInheritanceContextsFound = HasMultipleInheritanceContexts;  // We can never leave this state once there

            //
 

 
 

            bool newInheritanceContext = context.CanBeInheritanceContext && !this.IsInheritanceContextSealed;  // becomes false if we find context on the list 

            for (int i = 0; i < count; i++)
            {
                DependencyObject currentContext = (DependencyObject)list[i].Owner.Target; 
                if (currentContext != null)
                { 
                    if (currentContext == context) 
                    {
                        // insert after the last matching context 
                        insertIndx = i + 1;
                        newInheritanceContext = false;
                    }
 
                    if (newInheritanceContext && !multipleInheritanceContextsFound)
                    { 
                        if (currentContext != lastContext && currentContext.CanBeInheritanceContext)  // Count remaining inheritance contexts 
                        {
                            // We already found a previous inheritance context, so we have multiple ones 
                            multipleInheritanceContextsFound = true;
                            Freezable_HasMultipleInheritanceContexts = true;
                        }
                        lastContext = currentContext; 
                    }
                } 
                else 
                {
                    ++deadRefs; 
                }
            }

            list.Insert(insertIndx, new FreezableContextPair(context, property)); 

            PruneContexts(list, deadRefs); 
        } 

 
        private void PruneContexts(FrugalObjectList oldList, int numDead)
        {
            int count = oldList.Count;
 
            if (count - numDead == 0)
            { 
                RemoveContextList(); 
            }
            else if (numDead > 0) 
            {
                FrugalObjectList newList =
                    new FrugalObjectList(count - numDead);
 
                for (int i = 0; i < count; i++)
                { 
                    if (oldList[i].Owner.IsAlive) 
                    {
                        newList.Add(oldList[i]); 
                    }
                }

                ContextList = newList; 
            }
        } 
 
        /// 
        /// Helper function to get all of the event handlers for the Freezable and 
        /// place them in the calledHandlers list.
        ///  Where to place the change handlers for the Freezable. 
        /// 
        private void GetHandlers(ref EventStorage calledHandlers) 
        {
            if (Freezable_UsingSingletonHandler) 
            { 
                if (calledHandlers == null)
                { 
                    calledHandlers = GetEventStorage();
                }

                calledHandlers.Add(SingletonHandler); 
            }
            else if (Freezable_UsingHandlerList) 
            { 
                if (calledHandlers == null)
                { 
                    calledHandlers = GetEventStorage();
                }

                FrugalObjectList handlers = HandlerList; 

                for (int i = 0, count = handlers.Count; i < count; i++) 
                { 
                    calledHandlers.Add(handlers[i]);
                } 
            }
        }

        ///  
        /// Add the specified EventHandler
        ///  
        /// Handler to add 
        private void HandlerAdd(EventHandler handler)
        { 
            Debug.Assert(handler != null);

            if (Freezable_UsingSingletonHandler)
            { 
                ConvertToHandlerList();
            } 
 
            if (Freezable_UsingHandlerList)
            { 
                HandlerList.Add(handler);
            }
            else
            { 
                AddSingletonHandler(handler);
            } 
        } 

        ///  
        /// Remove the specified EventHandler
        /// 
        /// Handler to remove
        private void HandlerRemove(EventHandler handler) 
        {
            bool failed = true; 
 
            Debug.Assert(handler != null);
 
            if (Freezable_UsingSingletonHandler)
            {
                if (SingletonHandler == handler)
                { 
                    RemoveSingletonHandler();
                    failed = false; 
                } 
            }
            else if (Freezable_UsingHandlerList) 
            {
                FrugalObjectList handlers = HandlerList;
                int index = handlers.IndexOf(handler);
 
                if (index >= 0)
                { 
                    handlers.RemoveAt(index); 
                    failed = false;
                } 

                if (handlers.Count == 0)
                {
                    RemoveHandlerList(); 
                }
            } 
 
            if (failed)
            { 
                throw new ArgumentException(SR.Get(SRID.Freezable_UnregisteredHandler), "handler");
            }
        }
 
        //
        //  Removes the singleton handler the Freezable is storing and resets 
        //  any state indicating this. 
        //
        private void RemoveSingletonHandler() 
        {
            Debug.Assert(Freezable_UsingSingletonHandler);

            if (HasContextInformation) 
            {
              _contextStorage = ((HandlerContextStorage)_contextStorage)._contextStorage; 
            } 
            else
            { 
              _contextStorage = null;
            }

            Freezable_UsingSingletonHandler = false; 
        }
 
        // 
        //  Removes the handler list the Freezable is storing and resets
        //  any state indicating this. 
        //
        private void RemoveHandlerList()
        {
            Debug.Assert(Freezable_UsingHandlerList && HandlerList.Count == 0); 

            if (HasContextInformation) 
            { 
                _contextStorage = ((HandlerContextStorage)_contextStorage)._contextStorage;
            } 
            else
            {
                _contextStorage = null;
            } 

             Freezable_UsingHandlerList = false; 
        } 

        ///  
        /// Helper function to convert to using a list to store context information.
        /// The SingletonContext is inserted into the list.
        /// 
        private void ConvertToHandlerList() 
        {
            Debug.Assert(Freezable_UsingSingletonHandler); 
 
            EventHandler singletonHandler = SingletonHandler;
 
            // The list is initialized with capacity for 2 entries since we
            // know we have a 2nd handler to insert, hence the conversion
            // from the singleton handler state.
            FrugalObjectList list = new FrugalObjectList(2); 

            list.Add(singletonHandler); 
 
            if (HasContextInformation)
            { 
                ((HandlerContextStorage)_contextStorage)._handlerStorage = list;
            }
            else
            { 
                _contextStorage = list;
            } 
 
            Freezable_UsingHandlerList = true;
            Freezable_UsingSingletonHandler = false; 
        }

        //
        // helper function to add a singleton handler.  The passed in handler parameter 
        // will be stored as the singleton handler.
        // 
        private void AddSingletonHandler(EventHandler handler) 
        {
            Debug.Assert(!Freezable_UsingHandlerList && !Freezable_UsingSingletonHandler); 
            Debug.Assert(handler != null);

            if (HasContextInformation)
            { 
                HandlerContextStorage hps = new HandlerContextStorage();
 
                hps._contextStorage = _contextStorage; 
                hps._handlerStorage = handler;
 
                _contextStorage = hps;
            }
            else
            { 
                _contextStorage = handler;
            } 
 
            Freezable_UsingSingletonHandler = true;
        } 

        #endregion

        #region Private properties 

        //----------------------------------------------------- 
        // 
        //  Private properties
        // 
        //------------------------------------------------------

        //
        // The below properties help at getting the singleton/list for the context or change handlers. 
        // In all cases, if the other object exists (i.e. we want context, and there are also stored handlers),
        // then _contextStorage is HandlerContextStorage, so we need to get the data we want from that class. 
        // 

        ///  
        /// Returns the context list the Freezable has.  This function assumes
        /// that UsingContextList is true before being called.
        /// 
        private FrugalObjectList ContextList 
        {
            get 
            { 
                Debug.Assert(Freezable_UsingContextList && !Freezable_UsingSingletonContext,
                             "Must call UsingContextList before use"); 

                if (HasHandlers)
                {
                    HandlerContextStorage ptrStorage = (HandlerContextStorage)_contextStorage; 

                    return (FrugalObjectList)ptrStorage._contextStorage; 
                } 
                else
                { 
                    return (FrugalObjectList)_contextStorage;
                }
            }
 
            set
            { 
                Debug.Assert(Freezable_UsingContextList && !Freezable_UsingSingletonContext, 
                             "Must call UsingContextList before use");
 
                if (HasHandlers)
                {
                    ((HandlerContextStorage)_contextStorage)._contextStorage = value;
                } 
                else
                { 
                    _contextStorage = value; 
                }
            } 
        }

        /// 
        /// Returns the handler list the Freezable has.  This function assumes 
        /// the handlers for the Freezable are stored in a list.
        ///  
        private FrugalObjectList HandlerList 
        {
            get 
            {
                Debug.Assert(Freezable_UsingHandlerList && !Freezable_UsingSingletonHandler,
                                      "Must call UsingHandlerList before use");
 
                if (HasContextInformation)
                { 
                    HandlerContextStorage ptrStorage = (HandlerContextStorage)_contextStorage; 

                    return (FrugalObjectList)ptrStorage._handlerStorage; 
                }
                else
                {
                    return (FrugalObjectList)_contextStorage; 
                }
            } 
        } 

        ///  
        /// Returns the singleton handler the Freezable has.  This function assumes
        /// that UsingSingletonHandler is true before being called.
        /// 
        private EventHandler SingletonHandler 
        {
            get 
            { 
                Debug.Assert(Freezable_UsingSingletonHandler && !Freezable_UsingHandlerList,
                                      "Must call UsingSingletonHandler before use"); 

                if (HasContextInformation)
                {
                    HandlerContextStorage ptrStorage = (HandlerContextStorage)_contextStorage; 

                    return (EventHandler)ptrStorage._handlerStorage; 
 
                }
                else 
                {
                    return (EventHandler)_contextStorage;

                } 
            }
        } 
 
        /// 
        /// Returns the singleton context the Freezable has.  This function assumes 
        /// that UsingSingletonContext is true before being called.
        /// 
        private DependencyObject SingletonContext
        { 
            get
            { 
                Debug.Assert(Freezable_UsingSingletonContext && !Freezable_UsingContextList, 
                             "Must call UsingSingletonContext before use");
 
                if (HasHandlers)
                {
                    HandlerContextStorage ptrStorage = (HandlerContextStorage)_contextStorage;
 
                    return (DependencyObject)ptrStorage._contextStorage;
                } 
                else 
                {
                    return (DependencyObject)_contextStorage; 
                }
            }
        }
 
        /// 
        /// Returns/sets the singleton context property of the Freezable.  This 
        /// function assumes that UsingSingletonContext is true before being called. 
        /// 
        private DependencyProperty SingletonContextProperty 
        {
            get
            {
                Debug.Assert(Freezable_UsingSingletonContext && !Freezable_UsingContextList, 
                             "Must call UsingSingletonContext before use");
 
                return (DependencyProperty)_property; 
            }
        } 

        /// 
        /// Whether the Freezable has event handlers.
        ///  
        private bool HasHandlers
        { 
            get 
            {
                return (Freezable_UsingHandlerList || Freezable_UsingSingletonHandler); 
            }
        }

        ///  
        /// Whether the Freezable has context information.
        ///  
        private bool HasContextInformation 
        {
            get 
            {
                return (Freezable_UsingContextList || Freezable_UsingSingletonContext);
            }
        } 

        #endregion 
 
        #region InheritanceContext
 
        /// 
        ///     InheritanceContext
        /// 
        internal override DependencyObject InheritanceContext 
        {
            [FriendAccessAllowed] // Built into Base, also used by Core and Framework. 
            get 
            {
                if (!Freezable_HasMultipleInheritanceContexts) 
                {
                    if (Freezable_UsingSingletonContext)  // We have exactly one Freezable context
                    {
                        DependencyObject singletonContext = SingletonContext; 
                        if (singletonContext.CanBeInheritanceContext)
                        { 
                            return singletonContext; 
                        }
                    } 
                    else if (Freezable_UsingContextList)
                    {
                        // We have multiple Freezable contexts, but at most one context is valid
                        FrugalObjectList list = ContextList; 
                        int count = list.Count;
 
                        for (int i = 0; i < count; i++) 
                        {
                            DependencyObject currentContext = (DependencyObject)list[i].Owner.Target; 

                            if (currentContext != null && currentContext.CanBeInheritanceContext)
                            {
                                // This is the first and only valid inheritance context we should find 
                                return currentContext;
                            } 
                        } 
                    }
                } 

                return null;  // If we have gotten here, we have either multiple or no valid contexts
            }
        } 

        ///  
        ///     HasMultipleInheritanceContexts 
        /// 
        internal override bool HasMultipleInheritanceContexts 
        {
            [FriendAccessAllowed] // Built into Base, also used by Core and Framework.
            get { return Freezable_HasMultipleInheritanceContexts; }
        } 

        #endregion InheritanceContext 
 
        //
        // A simple class that is used when the Freezable needs to store both handlers and context info. 
        // The _handlerStorage and _contextStorage fields can store either a list or a direct
        // reference to the object - Freezable's Freezable_* flags (actually added to DependencyObject.cs)
        // can be used to test for which one to use.
        // 
        private class HandlerContextStorage
        { 
            public object _handlerStorage; 
            public object _contextStorage;
        } 

        //
        // A simple struct that stores a weak ref to a dependency object and a corresponding property
        // of that object. 
        //
        private struct FreezableContextPair 
        { 
            public FreezableContextPair(DependencyObject dependObject, DependencyProperty dependProperty)
            { 
                Owner = new WeakReference(dependObject);
                Property = dependProperty;
            }
 
            public readonly WeakReference Owner;
            public readonly DependencyProperty Property; 
        } 

        // 
        // A simple class that is used to cache the event handlers that are gathered during a call
        // to FireChanged.  Using this cache cuts down on the amount of managed allocations, which
        // improves the performance of Freezables.
        // 
        private class EventStorage
        { 
            public EventStorage(int initialSize) 
            {
                // check just in case 
                if (initialSize <= 0) initialSize = 1;

                _events = new EventHandler[initialSize];
                _logSize = 0; 
                _physSize = initialSize;
                _inUse = false; 
            } 

            // 
            //  Adds a new EventHandler to the storage.  In the case that more memory is needed, the cache
            //  size is doubled.
            //
            public void Add(EventHandler e) 
            {
                if (_logSize == _physSize) { 
                    _physSize *= 2; 
                    EventHandler[] temp = new EventHandler[_physSize];
 
                    for (int i = 0; i < _logSize; i++) {
                        temp[i] = _events[i];
                    }
 
                    _events = temp;
                } 
 
                _events[_logSize] = e;
                _logSize++; 
            }

            //
            // Clears the list but does not free the memory so that future uses of the 
            // class can reuse the space and not take an allocation performance hit.
            // 
            public void Clear() 
            {
                _logSize = 0; 
            }

            public int Count
            { 
                get
                { 
                    return _logSize; 
                }
            } 

            public int PhysicalSize
            {
                get 
                {
                    return _physSize; 
                } 
            }
 
            public EventHandler this[int idx]
            {
                get
                { 
                    return _events[idx];
                } 
 
                set
                { 
                    _events[idx] = value;
                }
            }
 
            //
            //  So that it's possible to reuse EventStorage classes, and so that if one is being used, another 
            //  person does not overwrite the contents (i.e. FireChanged causes someone else call their FireChanged), 
            //  an InUse flag is set to indicate whether someone is currently using this class.
            // 
            public bool InUse
            {
                get
                { 
                    return _inUse;
                } 
                set 
                {
                    _inUse = value; 
                }
            }

            EventHandler[] _events;         // list of events 
            int _logSize;                   // the logical size of the list
            int _physSize;                  // the allocated buffer size 
            bool _inUse; 
        }
 
        //-----------------------------------------------------
        //
        //  Debug fields
        // 
        //------------------------------------------------------
 
        #region Debug 

        // Verify a clone.  If isDeepClone is true we make sure that the cloned object is not the same as the 
        // original. GetAsFrozen and GetCurrentValueAsFrozen do not do deep clones since they will immediately
        // return any frozen originals rather than cloning them.
        private static void Debug_VerifyCloneCommon(Freezable original, object clone, bool isDeepClone)
        { 
            if (Invariant.Strict)
            { 
                Freezable cloneAsFreezable = (Freezable) clone; 

                Debug_VerifyInstance("CloneCore", original, cloneAsFreezable); 

                // Extra CloneCommon checks
                if (isDeepClone)
                { 
                    Invariant.Assert(clone != original, "CloneCore should not return the same instance as the original.");
                } 
 
                Invariant.Assert(!cloneAsFreezable.HasHandlers, "CloneCore should not have handlers attached on construction.");
 
                IList originalAsIList = original as IList;
                if (originalAsIList != null)
                {
                    // we've already checked that original and clone are the same type 
                    IList cloneAsIList = clone as IList;
 
                    Invariant.Assert(originalAsIList.Count == cloneAsIList.Count, "CloneCore didn't clone all of the elements in the list."); 

                    for (int i = 0; i < cloneAsIList.Count; i++) 
                    {
                        Freezable originalItemAsFreezable = originalAsIList[i] as Freezable;
                        Freezable cloneItemAsFreezable = cloneAsIList[i] as Freezable;
                        if (isDeepClone && cloneItemAsFreezable != null && cloneItemAsFreezable != null) 
                        {
                            Invariant.Assert(originalItemAsFreezable != cloneItemAsFreezable, "CloneCore didn't clone the elements in the list correctly."); 
                        } 
                    }
                } 
            }
        }

        private static void Debug_VerifyInstance(String methodName, Freezable original, Freezable newInstance) 
        {
            if (Invariant.Strict) 
            { 
                Invariant.Assert(newInstance != null, "{0} should not return null.", methodName);
                Invariant.Assert(newInstance.GetType() == original.GetType(), 
                    String.Format(System.Globalization.CultureInfo.InvariantCulture,
                        "{0} should return instance of same type. (Expected= '{1}', Actual='{2}')",
                        methodName, original.GetType(), newInstance.GetType()));
                Invariant.Assert(!newInstance.IsFrozen, "{0} should return a mutable instance. Recieved a frozen instance.", 
                    methodName);
            } 
        } 

        // Enumerates our FreezableContextPairs and (when we have full DP information) 
        // verifies that the context is still valid.
        private void Debug_DetectContextLeaks()
        {
            if (Invariant.Strict) 
            {
                if (Freezable_UsingSingletonContext) 
                { 
                    Debug_VerifyContextIsValid(SingletonContext, SingletonContextProperty);
                } 
                else if (Freezable_UsingContextList)
                {
                    FrugalObjectList contextList = ContextList;
 
                    for(int i = 0, count = ContextList.Count; i < count; i++)
                    { 
                        FreezableContextPair context = ContextList[i]; 
                        DependencyObject owner = (DependencyObject) context.Owner.Target;
 
                        if (!context.Owner.IsAlive)
                        {
                            // If the WeakReference is no longer alive the owner which
                            // was "using" this Freezable has been GC'ed. 
                            //
                            // There is no way to verify that this object was pointing 
                            // to us pre-collection, but in theory it was and we are 
                            // just waiting for compaction.
 
                            continue;
                        }

                        Debug_VerifyContextIsValid(owner, context.Property); 
                    }
                } 
            } 
        }
 
        // Verifies that the given owner/property pair constitutes a valid
        // inheritance context for this Freezable.  This is a no-op if the
        // property is null.
        private void Debug_VerifyContextIsValid(DependencyObject owner, DependencyProperty property) 
        {
            if (Invariant.Strict) 
            { 
                Invariant.Assert(owner != null,
                    "We should not have null owners in the ContextList/SingletonContext."); 

                if (property == null)
                {
                    // This context was not made through a DependencyProperty.  There is 
                    // nothing we can verify.
 
                    return; 
                }
 
                // If we have DP information for the context, we can verify that
                // the property on the owner is still referencing us.  Example:
                //
                //            (Pen.Brush) 
                //
                //              .-----. 
                //             '       v 
                //           Pen      Brush
                //             ^       . 
                //              '-----'
                //
                //              Context
                // 
                // If the owner's DP value does not point to us than we've leaked
                // a context. 
 
                DependencyObject ownerAsDO = (DependencyObject) owner;
                object effectiveValue = ownerAsDO.GetValue(property); 

                // There is a notable exception to the rule above, which is that
                // ResourceDictionaries create a context between the resource and
                // the FE which owns the resource. 
                //
                // In this case, the connection will be made via the pragmatic, 
                // but somewhat arbitrarily chosen VisualBrush.Visual DP. 
                //
                // See comments in ResourceDictionary.AddInheritanceContext.  Note 
                // that the owner will be the FE which owns the ResourceDictionary,
                // not the ResourceDictionary itself.

                bool mayBeResourceDictionary = 
                    property.Name == "Visual"
                    && property.OwnerType.FullName == "System.Windows.Media.VisualBrush" 
                    && owner.GetType().FullName != "System.Windows.Media.VisualBrush";    // ResourceDictionaries may not be owned by a VisualBrush. 

// 



 

 
 

 
            }
        }

        #endregion Debug 

        //------------------------------------------------------ 
        // 
        //  Private fields
        // 
        //-----------------------------------------------------

        #region Private Fields
 
        // For the common case of having only a single context, we use _property
        // to store the DependencyProperty that goes with the single context. 
        private DependencyProperty _property; 

 
        // initial size to make the EventStorage cache
        private const int INITIAL_EVENTSTORAGE_SIZE = 4;

        #endregion 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK