建構函式設計
更新:2007 年 11 月
建構函式是用來初始化型別及建立型別執行個體的特殊方法;型別建構函式是用來初始化型別中的靜態資料,Common Language Runtime (CLR) 可在建立任何型別執行個體之前呼叫型別建構函式。型別建構函式為 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# 編譯器) 不支援在結構上使用無參數的建構函式。
請勿在物件上的建構函式內呼叫虛擬成員。
呼叫虛擬成員會呼叫最具衍生性的覆寫,不論是否已經呼叫定義該項覆寫的型別之建構函式。下列程式碼範例將說明這個問題。當基底類別建構函式執行時,它會呼叫衍生類別成員,即使尚未呼叫衍生類別建構函式。此範例會印出 BadBaseClass,以顯示狀態欄位尚未被 DerivedFromBad 建構函式所更新。
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.
如需設計方針的詳細資訊,請參閱由 Krzysztof Cwalina 和 Brad Abrams 所著,並由 Addison-Wesley 於 2005 年發行的「Framework 設計方針:可重複使用之 .NET 程式庫的慣例、慣用語法和模式」一書。