コンストラクタのデザイン

更新 : 2007 年 11 月

コンストラクタは、型を初期化し、型のインスタンスを作成するために使用する特別なメソッドです。型コンストラクタは、型に含まれる静的データの初期化に使用します。共通言語ランタイム (CLR: Common Language Runtime) は、型のインスタンスを作成する前に型コンストラクタを呼び出します。型コンストラクタは static (Visual Basic では Shared) であり、パラメータを受け取ることができません。インスタンス コンストラクタは、型のインスタンスの作成に使用します。インスタンス コンストラクタの場合は、パラメータを受け取ることができますが、必須ではありません。パラメータなしのインスタンス コンストラクタは、既定のコンストラクタと呼ばれます。

次のガイドラインでは、コンストラクタの作成に関する推奨事項を示します。

コンストラクタのパラメータは、メイン プロパティを設定するためのショートカットとして使用してください。

コンストラクタを使用してプロパティを設定しても、プロパティを直接設定しても同じ結果になる必要があります。次のコード例に示す EmployeeRecord クラスは、コンストラクタを呼び出しても、プロパティを直接設定しても初期化できます。EmployeeManagerConstructor クラスは、コンストラクタを使用した EmployeeRecord オブジェクトの初期化を示しています。EmployeeManagerProperties クラスは、プロパティを使用した EmployeeRecord オブジェクトの初期化を示しています。Tester クラスは、使用した手法とは関係なく、オブジェクトが同じ状態になっていることを示しています。

Imports System
Imports 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 employeeIdValue as Integer
        private departmentValue as Integer

        Public Sub New()
        End Sub

        Public Sub New(id as Integer, department as Integer)
            Me.employeeIdValue = id
            Me.departmentValue = department
        End Sub 

        Public Property Department as Integer
            Get 
                Return departmentValue
            End Get
            Set 
                departmentValue = value
            End Set
        End Property

        Public Property EmployeeId as Integer
            Get 
                Return employeeIdValue
            End Get
            Set 
                employeeIdValue = value
            End Set
        End Property

        Public Sub DisplayData()
            Console.WriteLine("{0} {1}", EmployeeId, Department)
        End Sub
    End Class

    ' This Class creates Employee records by passing 
    ' argumemnts to the constructor.
    Public Class EmployeeManagerConstructor
        Dim employees as Collection(Of EmployeeRecord) =  _
            new Collection(Of EmployeeRecord)()

        Public Sub AddEmployee(employeeId as Integer, department as Integer)
            Dim record as EmployeeRecord = new EmployeeRecord(employeeId, department)
            employees.Add(record)
            record.DisplayData()
        End Sub
    End Class

    ' This Class creates Employee records by setting properties.
    Public Class EmployeeManagerProperties
        Dim employees as Collection(Of EmployeeRecord)=  _
        new Collection(Of EmployeeRecord)()
        Public Sub AddEmployee(employeeId as Integer, department as Integer)
            Dim record as EmployeeRecord = new EmployeeRecord()
            record.EmployeeId = employeeId
            record.Department = department
            employees.Add(record)
            record.DisplayData()
        End Sub
    End Class

    Public Class Tester
    ' The following method creates objects with the same state
        ' using the two different approaches.
        Public Shared Sub Main()
            Dim byConstructor as EmployeeManagerConstructor = _
                new EmployeeManagerConstructor()
            byConstructor.AddEmployee(102, 102)

            Dim byProperties as EmployeeManagerProperties = _
                new EmployeeManagerProperties()
            byProperties.AddEmployee(102, 102)
        End Sub
    End Class
End Namespace
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);
        }
    }
}

これらの例および適切にデザインされたライブラリでは、いずれの手法を使用してもオブジェクトが同じ状態で作成されます。開発者がどちらの手法を使用するかは重要ではありません。

単にプロパティを設定するためにコンストラクタ パラメータを使用する場合は、コンストラクタ パラメータとプロパティに同じ名前を使用してください。このようなパラメータとプロパティの間の違いは、大文字と小文字の違いだけにする必要があります。

このガイドラインについては、前の例に示しています。

適切な場合、インスタンス コンストラクタから例外をスローしてください。

メソッドと同様に、コンストラクタでは例外をスローし、処理する必要があります。コンストラクタでは特に、処理できない例外をキャッチしたり、隠したりしないようにする必要があります。例外の詳細については、「例外のデザインのガイドライン」を参照してください。

既定のパブリック コンストラクタが必要な場合は、クラスで明示的に宣言してください。

クラスが既定のコンストラクタをサポートする場合は、それを明示的に宣言するのが最適な手順です。一部のコンパイラでは、既定のコンストラクタが自動的にクラスに追加されますが、既定のコンストラクタを明示的に追加すると、コードの保守が簡単になります。また、パラメータを受け取るコンストラクタを追加することによって、コンパイラが出力を停止した場合でも、既定のコンストラクタの定義がそのまま維持されます。

既定のコンストラクタを構造体に配置するのは避けてください。

C# コンパイラを含む多くのコンパイラが、構造体でのパラメータなしのコンストラクタをサポートしていません。

コンストラクタ内部のオブジェクトで仮想メンバを呼び出さないでください。

仮想メンバを呼び出すと、最派生オーバーライドを定義している型のコンストラクタが呼び出されたかどうかに関係なく、最派生オーバーライドが呼び出されます。この問題を次のコード例に示します。基本クラスのコンストラクタを実行すると、派生クラスのコンストラクタが呼び出されていない場合でも、派生クラス メンバが呼び出されます。このコード例は、ビューステート フィールドが DerivedFromBad コンストラクタによって更新されていないことを示す BadBaseClass を出力します。

Imports System

Namespace Examples.DesignGuidelines.MemberDesign
    Public Class BadBaseClass

        Protected  state as String
        Public Sub New()

            state = "BadBaseClass"
            SetState()
        End Sub
        Public Overridable Sub SetState()
        End Sub
    End Class

    Public Class DerivedFromBad 
        Inherits BadBaseClass
        Public Sub New()

            state = "DerivedFromBad "
        End Sub

        Public Overrides Sub SetState()
            Console.WriteLine(state)
        End Sub
    End Class

    Public Class tester

        Public Shared Sub Main()

            Dim b as DerivedFromBad = new DerivedFromBad()
        End Sub
    End Class
End Namespace
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.

デザイン ガイドラインの詳細については、2005 年に Addison-Wesley から出版されている Krzysztof Cwalina、Brad Abrams 共著の『Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries』を参照してください。

参照

概念

型コンストラクタのデザイン

その他の技術情報

メンバのデザインのガイドライン

クラス ライブラリ開発のデザイン ガイドライン