Des objets c’est bien. Des actions sur ces objets, c’est encore mieux. Il nous manque encore les caractéristiques des objets. C’est là qu’interviennent les propriétés.
Sans le savoir, vous avez déjà utilisé des propriétés, par exemple dans le code suivant :
Code : C# | string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
for (int i = 0; i < jours.Length; i++)
{
Console.WriteLine(jours[i]);
}
|
Dans la boucle, nous utilisons
jours.Length. Nous utilisons en fait la propriété
Length du tableau « jours », un tableau étant bien sûr un objet.
Nous avons pu utiliser d’autres propriétés, par exemple dans l’instruction suivante :
Code : C# | List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
for (int i = 0; i < jours.Count; i++)
{
Console.WriteLine(jours[i]);
}
|
Ici,
Count est la propriété de la liste « jours ».
De la même façon, nous avons la possibilité de créer des propriétés sur nos classes pour permettre d’ajouter des caractéristiques à nos objets.
Par exemple, nous pouvons rajouter les propriétés suivantes à notre voiture : une couleur, une marque, une vitesse.
Il y a plusieurs façons de rajouter des caractéristiques à un objet. La première est d’utiliser des variables membres.
Les variables membres :
Ici en fait, un objet peut avoir une caractéristique sous la forme d’une variable publique qui fait partie de la classe. Pour ce faire, il suffit de définir simplement la variable à l’intérieur des blocs de code délimitant la classe et de lui donner la visibilité
public.
Rajoutons par exemple une chaine de caractères permettant de stocker la couleur de la voiture :
Code : C# | public class Voiture
{
public string Couleur;
}
|
Notez que j’ai rajouté les visibilités pour la classe et pour la variable.
Grâce à ce code, la chaine de caractères Couleur est désormais une caractéristique de la classe Voiture. Nous pourrons l’utiliser en faisant suivre notre objet de l’opérateur « . » suivi du nom de la variable. Ce qui donne :
Code : C# | Voiture voitureNicolas = new Voiture();
voitureNicolas.Couleur = "rouge";
Voiture voitureJeremie = new Voiture();
voitureJeremie.Couleur = "verte";
|
Cela ressemble beaucoup à ce que nous avons déjà fait. Nous utilisons le « . » pour accéder aux propriétés d’un objet.
Comme d’habitude, les variables vont pouvoir stocker des valeurs. Ainsi, nous pourrons avoir une voiture rouge pour Nicolas et une voiture verte pour Jérémie.
Notez ici qu’il s’agit bien de variables membres et non de vraies propriétés. En général, les variables d’une classe ne doivent jamais être publiques. Nous utiliserons rarement cette construction.
Les propriétés :
Les propriétés sont en fait des variables évoluées. Elles sont à mi-chemin entre une variable et une méthode.
Prenons cet exemple :
Code : C# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | public class Voiture
{
private int vitessePrivee;
public int Vitesse
{
get
{
return vitessePrivee;
}
set
{
vitessePrivee = value;
}
}
}
|
Nous pouvons voir que nous définissons dans un premier temps une variable privée, « vitessePrivee » de type entier. Comme prévu, cette variable est masquée pour les utilisateurs d’objets
Voiture. Ainsi, le code suivant :
Code : C# | Voiture voitureNicolas = new Voiture();
voitureNicolas.vitessePrivee = 50;
|
provoquera l’erreur de compilation désormais bien connue :
Code : Console | MaPremiereApplication.Voiture.vitessePrivee' est inaccessible en raison de son niveau de protection |
Par contre, nous en avons profité pour définir la propriété
Vitesse, de type
int. Pour ceci, nous avons défini une partie de la propriété avec le mot clé
get suivi d’un
return vitessePrivee. Et juste en dessous, nous avons utilisé le mot clé
set, suivi de
vitessePrivee = value.
Ce que nous avons fait, c’est définir la possibilité de lire la propriété grâce au mot clé
get. Ici, la lecture de la propriété nous renvoie la valeur de la variable privée. De la même façon, nous avons défini la possibilité d’affecter une valeur à la propriété en utilisant le mot clé
set et en affectant la valeur à la variable privée.
Les blocs de code délimités par
get et
set se comportent un peu comme des méthodes, elles ont un corps qui est délimité par des accolades et dans le cas du
get, elle doit renvoyer une valeur du même type que la propriété.
À noter que dans le cas du
set, «
value » est un mot clé qui permet de dire : « la valeur que nous avons affectée à la propriété ».
Prenons l’exemple suivant :
Code : C# | Voiture voitureNicolas = new Voiture();
voitureNicolas.Vitesse = 50;
Console.WriteLine(voitureNicolas.Vitesse);
|
La première instruction instancie bien sûr une voiture.
La deuxième instruction consiste à appeler le bloc de code
set de Vitesse qui met la valeur 50 dans la pseudo-variable
value qui est stockée ensuite dans la variable privée.
Lorsque nous appelons la troisième instruction, nous lisons la valeur de la propriété et pour ce faire, nous passons par le
get qui nous renvoie la valeur de la variable privée.
Ok, c’est super, mais dans ce cas, pourquoi passer par une propriété et pas par une variable ? Même s'il parait que les variables ne doivent jamais être publiques …
Eh bien parce que dans ce cas-là, la propriété peut faire un peu plus que simplement renvoyer une valeur. Et aussi parce que nous masquons la structure interne de notre classe à ceux qui veulent l’utiliser.
Nous pourrions très bien envisager d’aller lire la vitesse dans les structures internes du moteur, ou en faisant un calcul poussé par rapport au coefficient du vent et de l’âge du capitaine (ou plus vraisemblablement en allant lire la valeur en base de données). Et ici, nous pouvons tirer parti de la puissance des propriétés pour masquer tout ça à l’appelant qui lui n’a besoin que d’une vitesse.
Par exemple :
Code : C# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | private int vitessePrivee;
public int Vitesse
{
get
{
int v = vitesseDesRoues * rayon * coefficient; // ce calcul est complètement farfelu !
MettreAJourLeCompteur();
AdapterLaVitesseDesEssuieGlaces();
return v;
}
set
{
// faire la mise à jour des variables internes
MettreAJourLeCompteur();
AdapterLaVitesseDesEssuieGlaces();
}
}
|
En faisant tout ça dans le bloc de code
get, nous masquons les rouages de notre classe à l’utilisateur. Lui, il n’a besoin que d’obtenir la vitesse, sans s’encombrer du compteur ou des essuie-glaces.
Bien sûr, la même logique peut s’adapter au bloc de code qui permet d’affecter une valeur à la propriété,
set.
Il est également possible de rendre une propriété en lecture seule, c’est-à-dire non modifiable par les autres objets. Il pourra par exemple sembler bizarre de positionner une valeur à la vitesse alors qu’en fait, pour mettre à jour la vitesse, il faut appeler la méthode
AppuyerPedale(double forceAppui).
Pour empêcher les autres objets de pouvoir directement mettre à jour la propriété Vitesse, il suffit de ne pas déclarer le bloc de code
set et de ne garder qu’un
get. Par exemple :
Code : C# 1
2
3
4
5
6
7
8
9
10
11
12 | public class Voiture
{
private int vitessePrivee;
public int Vitesse
{
get
{
// faire des calculs ...
return vitessePrivee;
}
}
}
|
Ce qui fait que si nous tentons d’affecter une valeur à la propriété Vitesse depuis une autre classe, par exemple :
Code : C# | Voiture voitureNicolas = new Voiture();
voitureNicolas.Vitesse = 50;
|
Nous aurons l’erreur de compilation suivante :
Code : Console | La propriété ou l'indexeur 'MaPremiereApplication.Voiture.Vitesse' ne peut pas être assigné -- il est en lecture seule |
Le compilateur nous indique donc très justement qu’il est impossible de faire cette affectation car la propriété est en lecture seule.
Il devient donc impossible pour un utilisateur de cette classe de modifier la vitesse de cette façon.
De même, il est possible de définir une propriété pour qu’elle soit accessible en écriture seule. Il suffit de définir uniquement le bloc de code
set :
Code : C# | private double acceleration;
public double Acceleration
{
set
{
acceleration = value;
}
}
|
Ainsi, si nous tentons d’utiliser la propriété en lecture, avec par exemple :
Code : C# | Console.WriteLine(voitureNicolas.Acceleration);
|
Nous aurons l’erreur de compilation attendue :
Code : Console | La propriété ou l'indexeur 'MaPremiereApplication.Voiture.Acceleration' ne peut pas être utilisé dans ce contexte, car il lui manque l'accesseur get |
Ce bridage d’accès à des propriétés prend tout son sens quand nous souhaitons exposer nos objets à d’autres consommateurs qui n’ont aucun intérêt à connaitre la structure interne de notre classe. C’est un des principes de l’encapsulation.
Les propriétés auto-implémentées :
Les propriétés auto-implémentées sont une fonctionnalité que nous allons beaucoup utiliser. Il s’agit de la définition d’une propriété de manière très simplifiée qui va nous servir dans la grande majorité des cas où nous aurons besoin d’écrire des propriétés. Dans ce tutoriel, nous l’utiliserons très souvent.
Ainsi, le code suivant que nous avons déjà vu :
Code : C# 1
2
3
4
5
6
7
8
9
10
11
12 | private int vitesse;
public int Vitesse
{
get
{
return vitesse;
}
set
{
vitesse = value;
}
}
|
est un cas très fréquent d’utilisation de propriétés. Nous exposons ici une variable privée à travers les propriétés
get et
set. L’écriture du dessus est équivalente à la suivante :
Code : C# | public int Vitesse { get; set; }
|
Dans ce cas, le compilateur fait le boulot lui-même, il génère (dans le code compilé) une variable membre qui va servir à stocker la valeur de la propriété.
C’est exactement pareil, sauf que cela nous simplifie grandement l’écriture.
En plus, nous pouvons encore accélérer son écriture en utilisant ce qu’on appelle des «
snippets » qui sont des extraits de code. Pour les utiliser, il suffit de commencer à écrire un mot et Visual C# nous complète le reste. Un peu comme la complétion automatique sauf que cela fonctionne pour des bouts de code très répétitifs et très classiques à écrire.
Commencez par exemple à taper « prop », Visual C# nous propose plusieurs extraits de code :
Appuyez sur
tab ou
entrée pour sélectionner cet extrait de code et appuyez ensuite sur
tab pour que Visual C# génère l’extrait de code correspondant à la propriété auto-implémentée. Vous aurez :
Ici,
int est surligné et vous pouvez, si vous le souhaitez, changer le type de la propriété, par exemple
string. Appuyez à nouveau sur
tab et Visual C# surligne le nom de la propriété, que vous pouvez à nouveau changer. Appuyez enfin sur
entrée pour terminer la saisie et vous aurez une belle propriété auto-implémentée :
Vous avez pu remarquer qu’en commençant à taper « prop », Visual C# express vous a proposé d’autres extraits de code, par exemple « propfull » qui va générer la propriété complète telle qu’on l’a vue un peu plus haut.
D’autres extraits de code existent, comme « propg » qui permet de définir une propriété en lecture seule.
En effet, comme au chapitre précédent, il est possible de définir des propriétés auto-implémentées en lecture seule ou en écriture seule avec une écriture simplifiée. Dans le cas des propriétés auto-implémentées, il y a cependant une subtilité.
Pour avoir de la lecture seule, nous devons indiquer que l'affectation est privée, comme on peut le voir en utilisant l’extrait de code « propg ». Visual C# express nous génère le bout de code suivant :
Code : C# | public int Vitesse { get; private set; }
|
En positionnant une propriété d’écriture en privée, Visual C# express autorise la classe Voiture à modifier la valeur de Vitesse, que ce soit par une méthode ou par une propriété.
A noter que si nous n'avions pas mis
private set et que nous avions simplement supprimé le
set, alors la compilation aurait été impossible. En effet, il s’avère difficile d’exploiter une propriété auto-implémentée en lecture alors que nous n’avons pas la possibilité de lui donner une valeur. Ou inversement.
Alors, pourquoi nous avoir parlé de la possibilité de complètement supprimer la lecture ou l’écriture ? Ce n’est pas plus intéressant de toujours mettre la propriété en private ?
Si c’est une propriété auto-implémentée, évidemment que si. Par contre, si nous n’utilisons pas une propriété auto-implémentée et que nous utilisons une variable membre pour sauvegarder la valeur de la propriété, nous pourrons éventuellement modifier ou lire la valeur de la variable à partir d’une méthode ou d’une autre propriété.
Bref, maintenant que vous connaissez les deux syntaxes, vous pourrez utiliser la plus adaptée à votre besoin.
Voilà pour les propriétés.
À noter que quand nous avons beaucoup de propriétés à initialiser sur un objet, nous pouvons utiliser une syntaxe plus concise. Par exemple, les instructions suivantes :
Code : C# | Voiture voitureNicolas = new Voiture();
voitureNicolas.Couleur = "Bleue";
voitureNicolas.Marque = "Peugeot";
voitureNicolas.Vitesse = 50;
|
sont équivalentes à l’instruction :
Code : C# | Voiture voitureNicolas = new Voiture { Couleur = "Bleue", Marque = "Peugeot", Vitesse = 50 };
|
Les accolades servent ici à initialiser les propriétés au même moment que l’instanciation de l’objet.
Note : cela parait évident, mais il est bien sûr possible d’accéder aux propriétés d’une classe depuis une méthode de la même classe.