构造器是用来初始化类型并为类型创建实例的特殊方法。类型构造器被用来初始化类型中的静态数据。类型构造器在该类型的任何实例被创建之前都会通过公共语言运行时(CLR)被调用。类型构造器是静态(在 Visual Basic 中是 Shared)的并且不能够获取参数。而实例构造器则被用来创建类型的实例。实例构造器能够获取参数,但是这样做并不是必需的。并且没有参数的实例构造器会调用默认的构造器。
下列指导方针描述了创建构造器的最佳实践。
考虑提供简单的,理想的构造器。一个简单的构造器只有少量的参数,并且所有参数都是简单的类型或枚举。
如果预期操作的语义并没有直接映射到新实例的构造,或者如果在遵循构造器的设计指导方针时感觉到不自然,就考虑使用一个静态的工厂方法来替代构造器。
把构造器的参数作为设置主要参数的快捷方式来使用。
通过使用构造器来设置属性与直接设置属性是相同的。下列代码范例说明了一个既能够通过调用构造器来进行初始化又能够通过直接设置属性来进行初始化的 EmployeeRecord 类。EmployeeManagerConstructor 类示范了使用构造器来初始化一个 EmployeeRecord 对象。EmployeeManagerProperties 类示范了使用属性来初始化一个 EmployeeRecord 对象。而 Tester 类则示范了不管哪种方式被使用,对象都能够拥有相同的状态。
using System;
using System.Collections.ObjectModel;
namespace Examples.DesignGuidelines.Constructors
{
// 这个类既能够通过设置属性的方式又能够通过把数据传递到它的构造器的方式来获取它的数据。
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);
}
}
// 这个类通过把参量传递给构造器的方式来创建 Employee 记录。
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();
}
}
// 这个类通过设置属性的方式来创建 Employee 记录。
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
{
// 下列方法使用两个不同的方式来创建拥有相同状态的对象。
public static void Main()
{
EmployeeManagerConstructor byConstructor =
new EmployeeManagerConstructor();
byConstructor.AddEmployee(102, 102);
EmployeeManagerProperties byProperties =
new EmployeeManagerProperties();
byProperties.AddEmployee(102, 102);
}
}
}注意:在这些范例中,以及在一个设计良好的库中,所有方法都创建了拥有相同状态的对象。这与开发者所偏爱的使用方式无关。
为构造器的参数和属性使用相同的名称,如果构造器的参数被简单地用来设置属性。那么参数与属性之间唯一的区别就是转换。
这个指导方针已经在前面的范例中被说明。
最小化构造器的工作量。构造器不应该完成除了获取构造器的参数之外的大量工作。在不是必需的情况下,任何其他处理的代价都应该被延迟考虑。
从构造器的实例中适当地抛出异常。
构造器应该能够象方法一样抛出并且处理异常。尤其是:一个构造器不应该捕获并隐藏任何它无法处理的异常。关于异常的附加信息,请参考:[异常的设计指导方针]。
如果构造器是必需的,就在类中明确地声明公共的默认构造器。
如果你的类支持,那么明确地定义默认构造器就是一个最佳的实践。虽然一些编译器会自动为你的类添加一个默认构造器,但是明确地添加它能够让代码更加易于维护。这同样在因为你添加了一个获取参数的构造器而导致编译器停止自动产生构造器的时候,仍然能够确保默认构造器的定义是被保持的。
避免在结构中定义默认构造器。
多数编译器(包括 C# 编译器)都不支持在结构中定义无参数的构造器。
不要在构造器中调用它的虚拟成员。
不管定义了最常被派生的重载的类型的构造器是否被调用,对虚拟成员进行调用都会导致最常被派生的重载被调用。下列代码范例就示范了这个问题。与基类构造器的执行一样,它会调用被派生的类成员,尽管被派生的类构造器并没有被调用。这个范例输出 BadBaseClass 的内容来显示其状态字段并没有在 DerivedFromBad 构造器中被更新。
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();
}
}
}
浙公网安备 33010602011771号