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

コンストラクターは、型を初期化し、型のインスタンスを作成するために使用する特別なメソッドです。 型コンストラクターは、型に含まれる静的データの初期化に使用します。 共通言語ランタイム (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);
        }
    }
}
using namespace System;
using namespace System::Collections::ObjectModel;

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

    public:
        EmployeeRecord()
        {
        }

        EmployeeRecord(int id, int department)
        {
            this->employeeId = id;
            this->department = department;
        }

        property int Department
        {
            int get() {return department;}
            void set(int value) {department = value;}
        }

        property int EmployeeId
        {
            int get() {return employeeId;}
            void set(int value) {employeeId = value;}
        }

        void DisplayData()
        {
            Console::WriteLine("{0} {1}", EmployeeId, Department);
        }
    };

    // This class creates Employee records by passing 
    // argumemnts to the constructor.
    public ref class EmployeeManagerConstructor
    {
    private:
        Collection<EmployeeRecord^>^ employees;

    public:
        EmployeeManagerConstructor()
        {
            employees = gcnew Collection<EmployeeRecord^>();
        }

        void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord^ record = gcnew EmployeeRecord(employeeId, department);
            employees->Add(record);
            record->DisplayData();
        }
    };

    // This class creates Employee records by setting properties.
    public ref class EmployeeManagerProperties
    {
    private:
        Collection<EmployeeRecord^>^ employees;

    public:
        EmployeeManagerProperties()
        {
            employees = gcnew Collection<EmployeeRecord^>();
        }

        void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord^ record = gcnew EmployeeRecord();
            record->EmployeeId = employeeId;
            record->Department = department;
            employees->Add(record);
            record->DisplayData();
        }
    };

    public ref class Tester
    {
    // The following method creates objects with the same state
        // using the two different approaches.
    public:
        static void Main()
        {
            EmployeeManagerConstructor^ byConstructor =
                gcnew EmployeeManagerConstructor();
            byConstructor->AddEmployee(102, 102);

            EmployeeManagerProperties^ byProperties =
                gcnew 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();
        }
    }
}
using namespace System;

namespace Examples { namespace DesignGuidelines { namespace MemberDesign
{
    public ref class BadBaseClass
    {
    protected:
        String^ state;

    public:
        BadBaseClass()
        {
            state = "BadBaseClass";
            SetState();
        }

        virtual void SetState()
        {

        }
    };

    public ref class DerivedFromBad : public BadBaseClass
    {
    public:
        DerivedFromBad()
        {
            state = "DerivedFromBad ";
        }

        virtual void SetState() override
        {
            Console::WriteLine(state);
        }

    };

    public ref class tester
    {
    public:
        static void Main()
        {
            DerivedFromBad^ b = gcnew DerivedFromBad();
        }
    };
}}}

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

設計ガイドラインの詳細についてを参照してください、「フレームワークの設計ガイドライン。規則、慣用句、および再利用可能なパターン。ネット ライブラリ」本クシシュトフ Cwalina、ブラッド エイブラムス、アスキー、2005 年発表しました。

参照

概念

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

その他の技術情報

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

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