Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Omer van Kloeten's Facebook profile

Get Firefox

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

October 2005 - Posts

Bug: CodeDom Unable To Generate Static Events

I have just posted a bug on LadyBug regarding the issue in the title.
Please go and vote on this issue.

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.

More Posts