CodeDOM Singleton Pattern

I had this in my head for a while. I think it could be useful... for someone out there :)

using System;
using System.CodeDom;

namespace DotNetZen
{
    /// <summary>
    /// Represents the base declaration of a class as a Singleton.
    /// </summary>
    public class CodePatternSingleton : CodeTypeDeclaration
    {
        public const string InstanceFieldName = "instance";
        public const string InstancePropertyName = "Instance";
        public const string InnerClassName = "InstanceContainer";

        private CodeMemberField instanceField;
        private CodeMemberProperty instanceProperty;
        private CodeConstructor privateConstructor;
        private CodeTypeConstructor staticConstructor;
        private CodeTypeReference selfReference;

        /// <summary>
        /// Initializes a new instance of the CodePatternSingleton class.
        /// </summary>
        /// <param name="name">The name for the new type.</param>
        /// <param name="isLazyLoad">true if the singleton should load the value on first call; false if at type load.</param>
        public CodePatternSingleton(string name, bool isLazyLoad)
            : base(name)
        {
            // Create a self-reference for re-use.
            this.selfReference = new CodeTypeReference(name);

            // Initialize the static containing field.
            this.instanceField = new CodeMemberField(this.selfReference, InstanceFieldName);
            this.instanceField.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
            this.instanceField.Attributes |= MemberAttributes.Private | MemberAttributes.Static;

            // Initialize the static access property.
            this.instanceProperty = new CodeMemberProperty();
            this.instanceProperty.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
            this.instanceProperty.Attributes |= MemberAttributes.Public | MemberAttributes.Static;
            this.instanceProperty.HasGet = true;
            this.instanceProperty.HasSet = false;
            this.instanceProperty.Name = InstancePropertyName;
            this.instanceProperty.Type = this.selfReference;

            CodeObjectCreateExpression initializationExpression = new CodeObjectCreateExpression(this.selfReference);

            this.instanceField.InitExpression = initializationExpression;

            // If we lazy-load, we should use a nested class.
            // Otherwise, we should just assign it to the field.
            if (isLazyLoad)
            {
                CodeTypeDeclaration nestedClass = new CodeTypeDeclaration(InnerClassName);

                // Create a private constructor so no one could initialize an instance of the nested class.
                CodeConstructor nestedPrivateConstructor = new CodeConstructor();
                nestedPrivateConstructor.Attributes &= ~MemberAttributes.AccessMask;
                nestedPrivateConstructor.Attributes |= MemberAttributes.Private;

                // Create a static private constructor so that the nested class is not marked with beforefieldinit.
                CodeTypeConstructor nestedStaticConstructor = new CodeTypeConstructor();

                // Initialize the static access nested property.
                CodeMemberProperty nestedInstanceProperty = new CodeMemberProperty();
                nestedInstanceProperty.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
                nestedInstanceProperty.Attributes |= MemberAttributes.Public | MemberAttributes.Static;
                nestedInstanceProperty.HasGet = true;
                nestedInstanceProperty.HasSet = false;
                nestedInstanceProperty.Name = InstancePropertyName;
                nestedInstanceProperty.Type = this.selfReference;

                nestedInstanceProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, InstanceFieldName)));

                nestedClass.Members.Add(this.instanceField);
                nestedClass.Members.Add(nestedPrivateConstructor);
                nestedClass.Members.Add(nestedStaticConstructor);
                nestedClass.Members.Add(nestedInstanceProperty);

                this.instanceProperty.GetStatements.Add(
                        new CodeMethodReturnStatement(new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(InnerClassName), InstancePropertyName))
                    );

                this.Members.Add(nestedClass);
            }
            else
            {
                this.instanceProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, InstanceFieldName)));

                // Create a static private constructor so that the class is not marked with beforefieldinit.
                this.staticConstructor = new CodeTypeConstructor();

                this.Members.Add(this.instanceField);
                this.Members.Add(this.staticConstructor);
            }

            // Create a private constructor so no one could initialize an instance of the class.
            this.privateConstructor = new CodeConstructor();
            this.privateConstructor.Attributes &= ~MemberAttributes.AccessMask;
            this.privateConstructor.Attributes |= MemberAttributes.Private;

            this.Members.Add(this.privateConstructor);
            this.Members.Add(this.instanceProperty);
        }

        /// <summary>
        /// Gets or sets the name of the type.
        /// </summary>
        public new string Name
        {
            get
            {
                return base.Name;
            }
            set
            {
                base.Name = value;

                // If the name has changed, so should the singleton.
                this.selfReference.BaseType = value;
            }
        }
    }
}

Output (Foo is lazy, Bar isn't):

public class Foo
{
    private Foo()
    {
    }

    public static Foo Instance
    {
        get
        {
            return InstanceContainer.Instance;
        }
    }

    public class InstanceContainer
    {
        private static Foo instance = new Foo();

        static InstanceContainer()
        {
        }

        private InstanceContainer()
        {
        }

        public static Foo Instance
        {
            get
            {
                return instance;
            }
        }
    }
}

public class Bar
{
    private static Bar instance = new Bar();

    static Bar()
    {
    }

    private Bar()
    {
    }

    public static Bar Instance
    {
        get
        {
            return instance;
        }
    }
}

Updated to be thread safe (For an explanation go to Jon Skeet's article about Singletons) with two differences (property in lazy load instead of field and no readonly on fields) due to CodeDOM 1.1 limitations.

6 Comments

  • The resultant code is not thread safe.

  • Not thread safe and...why the f^*&amp; would you want to create that monstrosity for such a small amount of resultant code?



    Codedom is usefull for somethings. If you need to create a simple singleton, you need to type 200 characters instead of 5000.

  • foobar - You're right, I'll have to fix the code.



    Chris Martin - This is supposed to be a reusable and maintainable piece of code, so that if you are supposed to create many singletons, you could simply call on it to do the 'dirty work' for you. It's not supposed to appear each time you need a singleton. If you'd like it in DLL shape, just let me know and I'll post that for you.



    More suggestions are welcome.

  • You should use refly

    www.codeproject.com/csharp/refly.asp

  • It's overkill for a very simple pattern friend.

  • I think the point of this is to use it as a codegen template. It may be a little more useful to implement it as a VS.NET 2005 code snippet template.



    Just type &quot;sing&quot; and press tab and have the template expand out for you. :)

Comments have been disabled for this content.