Conception de constructeurs
Les constructeurs représentent des méthodes spéciales visant à initialiser des types et à créer des instances de types. Un constructeur de type sert à initialiser des données statiques dans un type. Il est appelé par le Common Language Runtime (CLR) avant de créer des instances du type. Les constructeurs de type sont static (Shared en Visual Basic) et ne peuvent pas prendre de paramètres. Un constructeur d'instance est utilisé pour créer des instances d'un type. Les constructeurs d'instance peuvent prendre des paramètres, mais ce n'est pas indispensable. Un constructeur d'instance sans paramètre est appelé constructeur par défaut.
Les règles suivantes décrivent les méthodes conseillées pour créer des constructeurs.
Privilégiez les constructeurs simples, voire les constructeurs par défaut. Un constructeur simple possède très peu de paramètres et tous les paramètres sont des types primitifs ou des énumérations.
Envisagez d'utiliser une méthode de fabrique statique au lieu d'un constructeur si la sémantique de l'opération requise ne correspond pas directement à la construction d'une nouvelle instance ou si les règles de conception du constructeur ne vous semblent pas naturelles.
Utilisez les paramètres des constructeurs en tant que raccourcis pour définir les propriétés principales.
La définition des propriétés à l'aide du constructeur doit être identique à la définition directe des propriétés. L'exemple de code suivant illustre une classe EmployeeRecord qui peut être initialisée soit par l'appel à un constructeur, soit par la définition directe des propriétés. La classe EmployeeManagerConstructor illustre l'initialisation d'un objet EmployeeRecord à l'aide du constructeur. La classe EmployeeManagerProperties illustre l'initialisation d'un objet EmployeeRecord à l'aide de propriétés. La classe Tester montre que les objets ont le même état, indépendamment de la technique utilisée.
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);
}
};
}}}
Notez que, dans ces exemples et dans une bibliothèque bien conçue, les deux approches créent des objets présentant le même état. L'approche choisie par le développeur importe peu.
Utilisez le même nom pour les paramètres de constructeur et une propriété, si les paramètres de constructeur sont utilisés pour définir simplement la propriété. La seule différence entre ces paramètres et les propriétés est la casse.
Cette règle est illustrée dans l'exemple précédent :
Limitez au maximum les opérations dans le constructeur. Les constructeurs ne devraient rien faire d'autre que capturer les paramètres de constructeur. Le coût de tout autre traitement doit être différé tant qu'il n'est pas indispensable.
Levez des exceptions à partir des constructeurs d'instances, le cas échéant.
Les constructeurs doivent lever et gérer des exceptions comme une méthode quelconque. En particulier, un constructeur ne doit pas intercepter ni masquer des exceptions qu'il ne peut pas gérer. Pour plus d'informations sur les exceptions, consultez Instructions de conception pour les exceptions.
Déclarez explicitement le constructeur public par défaut dans les classes, si un tel constructeur est nécessaire.
Il s'agit de la méthode conseillée pour définir explicitement un constructeur par défaut si votre classe prend cette opération en charge. Même si certains compilateurs ajoutent automatiquement un constructeur par défaut à votre classe, l'ajout explicite de celui-ci simplifie la maintenance du code. En outre, vous êtes assuré que le constructeur par défaut reste défini même si le compilateur cesse de l'émettre parce que vous ajoutez un constructeur avec des paramètres.
Évitez la présence de constructeurs par défaut sur les structures.
De nombreux compilateurs, y compris le compilateur C#, ne prennent pas en charge les constructeurs sans paramètre sur les structures.
N'appelez pas de membres virtuels sur un objet à l'intérieur de ses constructeurs.
Lors de l'appel à un membre virtuel, la substitution la plus dérivée est appelée, que le constructeur du type définissant la substitution la plus dérivée ait été appelé ou non. L'exemple de code suivant illustre ce problème. Au moment où le constructeur de la classe de base s'exécute, il appelle le membre de la classe dérivée, même si le constructeur de la classe dérivée n'a pas été appelé. Cet exemple imprime BadBaseClass pour montrer que le champ d'état n'a pas été mis à jour par le constructeur 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();
}
};
}}}
Portions Copyright 2005 Microsoft Corporation. Tous droits réservés.
Portions Copyright Addison-Wesley Corporation. Tous droits réservés.
Pour plus d'informations sur les règles de conception, consultez « règles de conception d'infrastructure : Conventions idiomes et modèles carnet de bibliothèques réutilisables framework » Krzysztof Cwalina et Brad Abrams, publiés par Addison-Wesley, 2005.
Voir aussi
Concepts
Conception de constructeurs de type
Autres ressources
Instructions de conception des membres
Instructions de conception pour le développement de bibliothèques de classes