Wednesday, April 27, 2011

WPF: DispatcherObject - Anatomy

In my last post, I was a bit surprised to see how DispatcherObject is implemented behind-the-scenes. Since this class is at the top of the WPF "Spine", I want to take time to understand all aspects of it, giving it the same level of attention I gave Object when I first started programming in .NET over ten years ago.

ILSpy gives us the following view of DispatcherObject, disclosing little more than the official MSDN documentation for this class:

using MS.Internal.WindowsBase;
using System;
using System.ComponentModel;
using System.Runtime;
namespace System.Windows.Threading
{
    public abstract class DispatcherObject
    {
        private Dispatcher _dispatcher;
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public Dispatcher Dispatcher...
        [FriendAccessAllowed]
        internal void DetachFromDispatcher()... ???
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool CheckAccess()...
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void VerifyAccess()...
        protected DispatcherObject()...
    }
}



First and foremost, we must acknowledge that DispatcherObject is abstract, so no direct instantiation of this class is possible. Naturally, this means the constructor would be protected.

From my last post, we saw that the constructor initializes the _dispatcher instance field by calling Dispatcher.CurrentDispatcher. Given my prior experience with the Dispatcher class, I would expect DispatcherObject's CheckAccess() and VerifyAccess() methods to leverage _dispatcher in their implementations, and indeed they do with one minor surprise:
// System.Windows.Threading.DispatcherObject
protected DispatcherObject()
{
    this._dispatcher = Dispatcher.CurrentDispatcher;

}

[EditorBrowsable(EditorBrowsableState.Advanced)]
public Dispatcher Dispatcher
{
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    get
    {
        return this._dispatcher;
    }

}

public bool CheckAccess()
{
    bool result = true;
    Dispatcher dispatcher = this._dispatcher;
    if (dispatcher !null)
    {
        result = dispatcher.CheckAccess();
    }
    return result;
}

[EditorBrowsable(EditorBrowsableState.Never)]
public void VerifyAccess()
{
    Dispatcher dispatcher = this._dispatcher;
    if (dispatcher !null)
    {
        dispatcher.VerifyAccess();
    }
}


The constructor gives us a reference to the Dispatcher for this thread, but CheckAccess() and VerifyAccess() both check to see if _dispatcher is null? I guess we should have expected this since _dispatcher is not declared readonly.

DetachFromDispatcher() undoes the work done by the constructor. The effect is permanent, since there is no way to set the value of _dispatcher again after this method is called:

[FriendAccessAllowed]
internal void DetachFromDispatcher()
{
    this._dispatcher = null;
}


Why do we need this? Almost all classes in the DispatcherObject WPF hierarchy want to have thread affinity, except for objects that derive from Freezable. When you tell a Freezeable object to Freeze(), it transforms into an immutable, free-threaded object:
// System.Windows.Freezable
internal bool Freeze(bool isChecking)
{
    if (isChecking)
    {
        this.ReadPreamble();
        return this.FreezeCore(true);
    }
    if (!this.IsFrozenInternal)
    {
        this.WritePreamble();
        this.FreezeCore(false);
        PropertyMetadata.RemoveAllCachedDefaultValues(this);
        DependencyObject.DependentListMapField.ClearValue(this);
        base.Freezable_Frozen = true;
        base.DetachFromDispatcher(); // lookee here!
        this.FireChanged();
        this.ClearContextAndHandlers();
        this.WritePostscript();
    }
    return true;
}


We will look more into Freezeable objects in later posts, but this usage of DetachFromDispatcher() is entirely expected. This also explains why you can't unfreeze a Freezable object.

[The Polish Ambassador: Lions in the Street -- YouTube]

References

  • Pawan Mishra's blog has a great overview of the Dispatcher class.

No comments:

Post a Comment