Share via


构造函数设计

构造函数是一类特殊的方法,用于初始化类型和创建类型的实例。 类型构造函数用于初始化类型中的静态数据。 类型构造函数由公共语言运行时 (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);
        }
    }
}
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# 编译器)不支持在结构中使用无参数构造函数。

不要在对象的构造函数中调用对象的虚成员。

无论是否调用了定义派生程度最高的重写的类型的构造函数,调用虚成员都会导致调用派生程度最高的重写。 下面的代码示例对此进行了演示。 基类构造函数执行时,即使尚未调用派生类的构造函数,也会调用派生类的成员。 此示例输出 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();
        }
    }
}
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();
        }
    };
}}}

部分版权所有 2005 Microsoft Corporation。 保留所有权利。

部分版权所有 Addison-Wesley Corporation。 保留所有权利。

设计指引的详细信息,请参阅"框架设计准则: 公约、 成语和可重复使用的模式。网络图书馆"书 Krzysztof Cwalina 和布拉德 · 艾布拉姆斯,2005年艾迪生 - 韦斯利,发表。

请参见

概念

类型构造函数设计

其他资源

成员设计准则

类库开发的设计准则