コンストラクタのデザイン
更新 : 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』を参照してください。