This post aims to provide a way to implement lazy initialization in a multi threaded environment using the SingleInstance(Of T) Class.

CS1010110-01A-BIG

When building applications for a mobile operating system such as Windows Phone 7 (WP7), you might want (at times) to defer the creation of large objects, specifically when this creation is going to increase memory consumption. While in the desktop CLR there is the Lazy(Of T) Class, when working on WP7, this class does not exist (at least not at the time of this writing).

I find it a very repetitive task to manually produce a single instance object:

  1. Make its constructor private.
  2. Write the code for initialization.
  3. Provide a getter method that returns the one and only instance.

While you cannot avoid step 2, it is possible to create a generic class that produces step 1 and step 3. Then, from the class constructor, you can pass the code that creates the object using a Func(TResult) Delegate.

Update: The first version of the SingleInstance(Of T) class provided an option to create an instance (Of T) using the thread pool. This option is removed in the current version.

SingleInstance(Of T) Class

using System;
using System.Threading;

public sealed class SingleInstance<T> where T : class
{
    private readonly Object m_lockObj = new Object();
    private readonly Func<T> m_delegate;
    private Boolean m_isDelegateInvoked;

    private T m_value;

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="SingleInstance&lt;T&gt;"/> class.
    /// </summary>
    public SingleInstance()
        : this(() => default(T)) { }

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="SingleInstance&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="delegate">The @delegate.</param>
    public SingleInstance(Func<T> @delegate)
    {
        m_delegate = @delegate;
    }

    /// <summary>
    /// Gets the instance.
    /// </summary>
    /// <value>The instance.</value>
    public T Instance
    {
        get
        {
            if (!m_isDelegateInvoked)
            {
                T temp = m_delegate();
                Interlocked.CompareExchange<T>(ref m_value, temp, null);

                Boolean lockTaken = false;

                try
                {
                    // WP7 does not support the overload with the
                    // Boolean indicating if the lock was taken.
                    Monitor.Enter(m_lockObj); lockTaken = true;

                    m_isDelegateInvoked = true;
                }
                finally
                {
                    if (lockTaken) { Monitor.Exit(m_lockObj); }
                }
            }

            return m_value;
        }
    }
}

The SingleInstance(Of T) class has many differences from the System.Lazy(Of T) class in the desktop CLR.

  • The System.Lazy(Of T) class takes a LazyThreadSafetyMode enumeration. This enumeration contains 3 members (None, PublicationOnly, ExecutionAndPublication). The SingleInstance(Of T) class uses the interlocked constructs to produce a single instance. This is similar with passing LazyThreadSafetyMode.ExecutionAndPublication in the System.Lazy(Of T) class.
  • The System.Lazy(Of T) class works with classes (reference types) and structs (value types). The value types are boxed internally. The SingleInstance(Of T) class works only with reference types.
  • Finally, the System.Lazy(Of T) class is written, tested and supported by Microsoft, while the SingleInstance(Of T) is not.

Keep in mind that the SingleInstance(Of T) class uses a Func(TResult) delegate. There is a known performance hit when calling delegates compared to direct method calls. (See the Delegates section here.)

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"