Export (0) Print
Expand All
3 out of 6 rated this helpful - Rate this topic

Constructor Design

Constructors are special methods used to initialize types and create instances of types. A type constructor is used to initialize static data in a type. A type constructor is called by the common language runtime (CLR) before any instances of the type are created. Type constructors are static (Shared in Visual Basic) and cannot take parameters. An instance constructor is used to create instances of a type. Instance constructors can take parameters, but are not required to do so. An instance constructor with no parameters is called a default constructor.

The following guidelines describe the best practices for creating constructors.

Consider providing simple, ideally default, constructors. A simple constructor has a very small number of parameters, and all parameters are primitive types or enumerations.

Consider using a static factory method instead of a constructor if the semantics of the desired operation do not map directly to the construction of a new instance, or if following the constructor design guidelines feels unnatural.

Do use constructor parameters as shortcuts for setting main properties.

Setting properties by using the constructor should be identical to setting the properties directly. The following code example shows an EmployeeRecord class that can be initialized either by calling a constructor or by setting properties directly. The EmployeeManagerConstructor class demonstrates initializing an EmployeeRecord object using the constructor. The EmployeeManagerProperties class demonstrates initializing an EmployeeRecord object using properties. The Tester class demonstrates that regardless of the technique used, the objects have the same state.


using System;
using System.Collections.ObjectModel;
namespace Examples.DesignGuidelines.Constructors
{
    // This class can get its data either by setting 
    // properties or by passing the data to its constructor.
    public class EmployeeRecord
    {
        private int employeeId;
        private int department;

        public EmployeeRecord()
        {
        }
        public EmployeeRecord(int id, int department)
        {
            this.employeeId = id;
            this.department = department;
        }
        public int Department
        {
            get {return department;}
            set {department = value;}
        }
        public int EmployeeId
        {
            get {return employeeId;}
            set {employeeId = value;}
        }
        public void DisplayData()
        {
            Console.WriteLine("{0} {1}", EmployeeId, Department);
        }
    }
    // This class creates Employee records by passing 
    // argumemnts to the constructor.
    public class EmployeeManagerConstructor
    {
        Collection<EmployeeRecord > employees = new Collection<EmployeeRecord>();

        public void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord record = new EmployeeRecord(employeeId, department);
            employees.Add(record);
            record.DisplayData();
        }
    }
    // This class creates Employee records by setting properties.
    public class EmployeeManagerProperties
    {
    Collection<EmployeeRecord > employees = new Collection<EmployeeRecord>();
        public void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord record = new EmployeeRecord();
            record.EmployeeId = employeeId;
            record.Department = department;
            employees.Add(record);
            record.DisplayData();
        }
    }
    public class Tester
    {
    // The following method creates objects with the same state
        // using the two different approaches.
        public static void Main()
        {
            EmployeeManagerConstructor byConstructor = 
                new EmployeeManagerConstructor();
            byConstructor.AddEmployee(102, 102);

            EmployeeManagerProperties byProperties = 
                new EmployeeManagerProperties();
            byProperties.AddEmployee(102, 102);
        }
    }
}


Note that in these examples, and in a well-designed library, both approaches create objects in the same state. It does not matter which approach a developer prefers to use.

Do use the same name for constructor parameters and a property, if the constructor parameters are used to simply set the property. The only difference between such parameters and the properties should be casing.

This guideline is illustrated in the previous example.

Do minimal work in the constructor. Constructors should not do much work other than to capture the constructor parameters. The cost of any other processing should be delayed until required.

Do throw exceptions from instance constructors if appropriate.

Constructors should throw and handle exceptions like any method. Specifically, a constructor should not catch and hide any exceptions that it cannot handle. For additional information on exceptions, see Design Guidelines for Exceptions.

Do explicitly declare the public default constructor in classes, if such a constructor is required.

It is a best practice to explicitly define a default constructor if your class supports it. Even though some compilers automatically add a default constructor to your class, adding it explicitly makes code maintenance easier. It also ensures the default constructor remains defined even if the compiler stops emitting it because you add a constructor that takes parameters.

Avoid having default constructors on structures.

Many compilers, including the C# compiler, do not support parameterless constructors on structures.

Do not call virtual members on an object inside its constructors.

Calling a virtual member causes the most-derived override to be called regardless of whether the constructor for the type that defines the most-derived override has been called. The following code example demonstrates this issue. As the base class constructor executes, it calls the derived class member, even though the derived class constructor has not been called. This example prints BadBaseClass to show that the state field has not been updated by the DerivedFromBad constructor.


using System;

namespace Examples.DesignGuidelines.MemberDesign
{
    public class BadBaseClass
    {
        protected string state;
        public BadBaseClass()
        {
            state = "BadBaseClass";
            SetState();
        }
        public virtual void SetState()
        {

        }
    }

    public class DerivedFromBad : BadBaseClass
    {
        public DerivedFromBad()
        {
            state = "DerivedFromBad ";
        }
        public override void SetState()
        {
            Console.WriteLine(state);
        }

    }
    public class tester
    {
        public static void Main()
        {
            DerivedFromBad b = new DerivedFromBad();
        }
    }
}


Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2014 Microsoft. All rights reserved.