[C#]GET & SET & Match

Bien le bonjour à tous!

Ceux qui passent leur jopurnée à briser leur productivité sur le channel #cafzone ont deja eu vent de mes problemes de compréhension…

Voilà ce à quoi je me heurte depuis quelques jours…

Je suis débutant en POO pure ( sous entendu , que je connais bien ActionScript 2 mais c’est pas non plus de la POO au sens pure du terme , meme si ça s’en rapproche tres beaucoup ). Je me pose la question : « Pourquoi utiliser GET et SET pour acceder à un champ d’un objet ? »

En fait c’est la partie emmergée de l’iceberg … Dans la pratique je veux bien admettre qu’il faille créer des accesseurs , enfin des methodes publiques pour acceder à des champs privés. Je peux l’admettre , coder de cette façon là, pas de probleme… Mais j’ai aussi besoin de comprendre pourquoi ! :stuck_out_tongue:

Pourquoi ne pas utiliser des champs publiques , modifiable tout simplement par :
monObjet.monChamp = ploumploum;
Le champ étant public…

On me dit de partout que c’est porc comme ça … ok c’est porc , il faut pas le faire , je l’admet, mais on en revient toujours à « Pourquoi !? » :stuck_out_tongue:

On m’a donné plusieurs reponses hier, mais je dois avouer que ca ne change rien à mon incomprehension …

La partie immergée de l’iceberg serait donc:
« Quelle est la vraie utilité de la visibilité des variables ? »
« Rendre des variables private, protected ou public , est-ce vraiment necessaire si on fait un code propre? »
« Quel est le risque derriere tout cela si on fait mal les choses ? »

Comme je l’ai deja dit , je veux bien admettre qu’il faille proceder d’une certaine façon , mais j’ai aussi besoin de comprendre pourquoi.
Tel un petit lapin rose, je dois mettre les mains sur les portes du RER pour comprendre pourquoi on me dit de ne pas le faire ( non je ne suis pas Steve-O des jackass , enfin à un degré moindre quand meme ).

Je sais que c’est quand meme un gros morceau , et surtout une des bases de la POO, c’est pourquoi, avant d’accoucher de sacs à bugs, j’aimerait vraiment franchir ce cap :stuck_out_tongue:

Merci d’avance à ceux qui prendront la peine de me repondre !

reponse partielle:

parceque cette methode permet dans tes set de verifier des valeurs, ou meme de stocker differement que ce que passe l utilisateur.

(cas tout con: il te passe un char * mais toi tu veux stocker ca en string parcequ en interne c est plus facile a traiter pour toi)

et ca permet surtout d avoir un code plus safe et propre, c est a dire qu en verifiant tes valeurs dans tes set tu SAIS qu elles sont valides donc que tu n as pas besoin de faire tout un tas de verif que tu aurais a faire autrement pour ne pas avoir un code qui part en live.

D’une manière plus théorique, les get/set font partie d’une technique appelée “encapsulation”. Cette stratégie consiste à découpler la logique interne de la classe (c’est à dire les opérations qui n’ont pas besooin d’être connues à l’extérieur de la classe) de sa logique “externe”.

Maintenant tu as plusieurs niveaux : interne à la classe (private), interne au package(Java) ou namespace(C++, C#) avec “protected”, et vraiment disponible pour tout le monde (public). Ca permet de peaufiner ce que tu as besoin de donner à d’autres parties de l’application.

Bien entendu suivant le langage, t’as encore beaucoup d’autres niveaux possibles.

Car si par exemple tu mets ton champ “état” à public, alors tu peux y accéder à n’importe quel moment dans le reste de l’application. Ce qui peut améner des bugs parce que si ça se trouve, il ne faut pas qu’on puisse mettre le champ état à “2” quand il est actuellement à “3”. Et donc y accéder par get et set ça te permet de mettre tes règles dans les méthodes get et set, ça structure ton code.
Et si un autre développeur reprend ce que tu as fait, il saura où trouver cette logique.

[quote name=‘cronofab’ date=’ 19 Nov 2004, 12:46’]…Et donc y accéder par get et set ça te permet de mettre tes règles dans les méthodes get et set, ça structure ton code…
[right][post=“304829”]<{POST_SNAPBACK}>[/post][/right][/quote]

Pour revenir la dessus , donc on peut coller un tas d’autres methodes au sein d’un SET ou d’un GET , sans que cela fasse crado ?

[quote name=‘Monsieur_Max’ date=’ 19 Nov 2004, 13:00’]Pour revenir la dessus , donc on peut coller un tas d’autres methodes au sein d’un SET ou d’un GET , sans que cela fasse crado ?
[right][post=“304836”]<{POST_SNAPBACK}>[/post][/right][/quote]
Bien sûr, c’est l’intérêt de passer par des méthodes.
Plus généralement, ça permet de coupler ‘n’ actions lors de l’accès/modif d’une variable membre. Classiquement ça permet de contrôler le domaine de valeur, c.a.d. de vérifier que la nouvelle valeur souhaitée par l’appelant est bien dans l’intervalle autorisé mais ça ouvre tout un tas d’autres perspectives.

Tiens j’ai une remarque un peut HS, mais en Java quand on fait un :

monVelo.getRoue()
avec la fonction getRoue de type
public Roue getRoue(){
return this.roue;
}

il renvoie la référence de l’objet roue de monVelo, donc si on fait
roue1 = monVelo.getRoue()
et
roue1 = new Roue()
ca modifie la roue de monVelo !

C’est pareil en c# ?

(je sais, on pourrait faire this.roue.clone())

[quote name=‘kaneloon’ date=’ 19 Nov 2004, 05:14’]Tiens j’ai une remarque un peut HS, mais en Java quand on fait un :

monVelo.getRoue()
avec la fonction getRoue de type
public Roue getRoue(){
return this.roue;
}

il renvoie la référence de l’objet roue de monVelo, donc si on fait
roue1 = monVelo.getRoue()
et
roue1 = new Roue()
ca modifie la roue de monVelo !

C’est pareil en c# ?

(je sais, on pourrait faire this.roue.clone())
[right][post=“304877”]<{POST_SNAPBACK}>[/post][/right][/quote]

Hein? Je suis pas sur d’avoir pige, mais une chose est sure.
Le code suivant en C#, produit 0 comme prevu. Et je suis certain que ca fait la meme chose en Java…

[code]class Program {
public class Roue {
private int myVar;
public int MyProperty {
get { return myVar; }
set { myVar = value; }
}
}
public class Velo {
private Roue myRoue;
public Velo() {
myRoue = new Roue();
}

public Roue MyRoue {
  get { return myRoue; }
  set { myRoue = value; }
}

public override string ToString() {
  return myRoue.MyProperty.ToString();
}

}

static void Main(string[] args) {
Velo monVelo = new Velo();
Roue maRoueLocale = monVelo.MyRoue; // on cree un pointeur local maRoueLocale qui pointe sur l’instance monVelo.MyRoue
maRoueLocale = new Roue(); // le pointeur local pointe sur une nouvelle instance de Roue
maRoueLocale.MyProperty = 2; // je modifie la nouvelle instance, l’instance de monVelo reste intacte
Console.WriteLine(monVelo.ToString()); // produit 0 comme prevu
Console.ReadLine();
}
}[/code]

Déjà, clarifions que get et set du C# permet simplement d’utiliser de remplacer les fonctions get_x, get_y, set_x, set_y qu’on aurait utilisé en C++ pour faire exactement la même chose… On remplace « left=carre.get_x1() » par « left=carre.x1 » et on remplace « carre.set_x1(left) » par « carre.x1=left ». Ca ne sert, en fait, qu’à rendre le code « plus beau » et un peu plus lisible qu’en C++. C’est pas une révolution, mais ca fait pour beaucoup dans mon appréciation forte du C#

Ensuite « pourquoi utiliser des get_ et set_ au lieu de mettre les variables en publique ». Imagine un carré pour lequel, pour des raisons de performances obscures, tu vas décider de mettre en variable non seulement x1,x2,y1,y2, mais aussi width, height, area, perimeter.
Si tu ne mets pas de fonctions set_, ca veut dire que ton carré ne va pas gérer la modification de ces variables. Si un mec fait carre.x1=30, ca va pas modifier width. Pour que la classe carre gere elle meme ces modification, tu es obligé de créer set_x1, set_x2, set_y1 et set_y2.
Ensuite, pour récuperer les valeurs des variables tu pourrais être tenté de décider de mettre toutes tes variables en public, prétextant que ca t’évitera de créer des get_x1 & co, sans oublier get_width & co.

Seulement voilà, tu es un gros PORC ! :stuck_out_tongue: Et le mec qui va bosser avec toi sur ce projet enorme (parceque bon, le carré c’est simple là), comment il va deviner qu’il faut utiliser set_x2 pour changer la largeur et pas modifier directement width (pourtant visible puisque public) sans avoir une grosse incohérence pas belle ? Va falloir qu’il se plonge dans ton code… en fait, ta classe carré ne ressemblera alors en rien à une boite noire, et c’est pourtant ca la but de la conception orientée objet : faire des boites (ou briques) qui ont un rôle, des entrées, des sorties, mais dont le fonctionnement interne n’a pas a être connu de tous (non pas que ce soit secret, mais qu’on soit en droit de s’en foutre royalement tant qu’on a pas à gratter dedans).

A cela, il faut ajouter le fait que si tu mets tes variables en public, et qu’un collègue y met nawak et fait planter une fonction de la classe carré, soit tranquille que ce sera de ta faute. En entreprise, coder tout en public est (à mon sens) au moins aussi grave que de mal documenter.

Voilà, j’espere t’avoir aidé à comprendre la logique de tout cela

PS : selon certains défenseurs du « pure objet », le protected (qui permet de rendre une variable accéssible aux classes dérivées) est une hérésie. C’est logique et parfaitement justifié, mais bon, parfois on peut s’autoriser une petite entorse pourvu qu’on documente très bien.

[quote name=‹ kaneloon › date=’ 19 Nov 2004, 14:14’]…il renvoie la référence de l’objet roue de monVelo, donc si on fait
roue1 = monVelo.getRoue()
et
roue1 = new Roue()
ca modifie la roue de monVelo !

C’est pareil en c# ?

(je sais, on pourrait faire this.roue.clone())
[right][post=« 304877 »]<{POST_SNAPBACK}>[/post][/right][/quote]

Non, non, comme dit Glop, ça change pas la roue du vélo.

Par contre, si tu fais un :
monVelo.setRoue(new Roue());

là ça change la roue du vélo. Et au final, toi qui croyait avoir la roue du vélo dans roue1, ben tu t’es mis le doigt dans l’oeil…
Heureusement qu’en Java il y a synchronise qui permet de locker ton vélo pendant que tu travailles sur sa roue :wink:

[quote name=‘urdle’ date=’ 19 Nov 2004, 14:05’]PS : selon certains défenseurs du “pure objet”, le protected (qui permet de rendre une variable accéssible aux classes dérivées) est une hérésie. C’est logique et parfaitement justifié, mais bon, parfois on peut s’autoriser une petite entorse pourvu qu’on documente très bien.
[right][post=“305099”]<{POST_SNAPBACK}>[/post][/right][/quote]
Heu, le protected sur un champ, pas sur une propriete ou une fonction. Dans ce cas la ca a parfaitement son sens meme en pur objet 100% garanti pur. Par contre sur un champ prive qu’on rend protected, la c’est clair, c’est pas beau.

Sinon get et set tout depend de ce que tu veux faire. Dans 99% des cas tu veux un propriete pour acceder a ton champ prive, ne serais ce que pour faire des check de validite. Est ce que la valeur que tu essaye de fourrer dans ce champ a un sens? Peut etre que tu veux un get et pas set sur ta propriete.

Aussi dans une classe bien faite en general, tu veux aussi des evenements quand une propriete change. Et dans ce cas la tu veux lancer l’evenement que si la valeur que tu met est differente de la valeur que tu as deja. Donc

public int MaValeur { &nbsp;get { return maValeurPrivate; } &nbsp;set { &nbsp; &nbsp;if(maValeurPrivate != value) { &nbsp; &nbsp; &nbsp;maValeurPrivate = value; &nbsp; &nbsp; &nbsp;OnMaValeurChanged(); &nbsp; &nbsp;} &nbsp;} }
Meme si tu l’as pas maintenant, tu le voudra peut etre plus tard. Une indirection est un prix faible a payer pour se donner cette flexibilite et est souvent completement acceptable.

Maintenant si tu as une classe qui sert a faire du marshalling et c’est tout par exemple, qui recoit des valeurs d’ailleurs et qui sert juste de “container” pour des donnees et n’a aucune raison d’avoir de la logique en interne (rare mais ca arrive). Il y a aucune raison de se priver d’avoir (voir apres) par religion. Mais faut bien etre conscient de ce qu’on fait et des limitations qu’on se met pour le futur.

public class Point { &nbsp; &nbsp;public int X; &nbsp; &nbsp;public int Y; }

j’reponds à coté, c’est du HS et mon post précédent est assez long :stuck_out_tongue:
alors effectivement C# et Java ont un comportement différent dans le cas précisé.

Pour que cela fonctionne en c# comme en Java, il faudrait que getRoue() renvoie une référence à this.roue.
A ce niveau, ce n’est plus sale, c’est carrément dégueulasse. Ca fait d’ailleurs partie des griefs que j’ai contre Java, parceque cela implique qu’un développeur innattentionné va permettre de modifier un membre private de sa classe (bah caca), alors qu’en C# il faut explicitement demander à renvoyer une référence pour avoir ce comportement (on le fait donc en connaissance de cause… et c’est pas beau vialin d’ailleurs :stuck_out_tongue: ).

D’après ce que j’ai cru comprendre des mécanismes interne, C# ne fera un clone de cette classe que si la variable, dans une des références est modifiée (je suis pas sur d’être clair :stuck_out_tongue: )

[edit : et +1 sur tout ce qu’à dit glop :stuck_out_tongue: ]

Je crois que tu melange un peu les passage par valeur, les types immutables, et les types qui se comportent par defaut par reference et par valeur, ou je comprend mal ou tu t’exprime pas bien ou un mix des trois :P. La c’est pas super clair pour moi ce que tu veux dire en tout cas :stuck_out_tongue:

[quote name=‹ GloP › date=’ 19 Nov 2004, 23:20’]Je crois que tu melange un peu les passage par valeur, les types immutables, et les types qui se comportent par defaut par reference et par valeur, ou je comprend mal ou tu t’exprime pas bien ou un mix des trois :P. La c’est pas super clair pour moi ce que tu veux dire en tout cas :stuck_out_tongue:
[right][post=« 305107 »]<{POST_SNAPBACK}>[/post][/right][/quote]
Ou mon niveau en c# est pas assez balèze pour connaitre les types immutables… eeeer
ta fonction getRoue(), elle pourrait bien renvoyer un référence à this.roue. A partir de ce moment si tu fais (warning, je sais plus comment on déclare une référence en c#)
ref maRoue=monVelo.getRoue();
et là, si tu fais
maRoue=new Roue();
alors ca modifiera monVelo.roue (et ce au mépris du caractère privé de monVelo.roue)

clair ? juste ? faux ?

Bon, une chose est sure , j’ai ma reponse concernant le tas de petites questions qui me tire-lapinaient depuis quelques jours dans la tete !

Je remercie bien bas Moktar, Kzi, Cronofab et urdle pour l’aide apportée dans cette compréhension de la chose :stuck_out_tongue: et aussi indirectement pour leurs extensions du débat kaneloon, mccricri et notre maitre à tous dans Seg Fault : Glop :stuck_out_tongue:

Ce qui est sur c’est que j’aime le C# et que’avec le temps et l’apprentissage je sais qu’en cas de dilemne , j’aurai toujours un pti coin de net où il fera bon vomir mes questions :stuck_out_tongue: ( oh c’est beauuuu ! )

Bien bien bien, je me suis mal exprimé donc voilà mon exemple :

  • classe Vélo :

[code]class Velo {
private Roue maRoue;
public Velo(int tailleRoue){
this.maRoue = new Roue(tailleRoue);

}

public Roue getRoue(){

return this.maRoue;
}

public String toString(){

return (“Velo” + " Roue = "+ maRoue.getTaille());
}
}[/code]

  • Classe Roue :

[code]class Roue {
private int maTaille;

public Roue(int taille){
  maTaille = taille;
}
public int getTaille(){

return maTaille;
}
public void setTaille(int taille){
maTaille = taille;
}
}[/code]

Classe de Test :

[code]public class Test {

public static void main(String[] args) {
	Velo monVelo = new Velo(15);
	System.out.println(monVelo.toString());
	(monVelo.getRoue()).setTaille(10);
	System.out.println(monVelo.toString());
	
}

}[/code]
Ce code me renvoit bien :
Velo Roue = 15
Velo Roue = 10

Alors que je suis bien passé par getRoue() pour accéder à l’objet Roue de monVelo, et cela l’a bien modifié. Donc oui, quand on renvoit un objet, on renvoie sa référence non protégée (en Java au mois).

Heu oui c’est un peu evident non? (si si) Enfin moi il y a rien du tout qui me choque dans ce resultat/comportement…

C’est pour ca que de la meme maniere readonly sur un type par reference (pas un value type genre int ou long ou float mais un type “boxed”) ca fait pas forcement ce a quoi on s’attend au premier abord. D’ailleurs fxcop (l’analyseur de code pour la securite et les erreurs trouvable statiquement pour .net) va te prevenir que ton readonly il faut y faire gaffe, il va pas te servir a grand chose si tu peux modifier ce qui est a l’interieur de ton instance…

C’est pareil partout, si tu renvoie un pointeur vers une classe, tu renvoie un pointeur vers une classe, si tu modifie un champ a l’interieur tu modifie l’instance en question, meme si le pointeur lui meme est readonly. Ca me parait clair que si tu veux une copie il va falloir manuellement le cloner (en profondeur meme sinon tu auras copie que la premiere couche d’instace qui est le defaut d’un Clone()).

Enfin c’est pareil dans tous les langages du monde, si tu as une indirection, le fait que tu puisse que lire s’applique a l’indirection, pas a ce qu’il y a derriere, ou alors il te faut le faire a toutes les couches, je vois pas ou est le probleme. Tu trouveras aucun langage repute qui se comporte pas de cette maniere. Ca serait grave incomprehensible si ca se comportait autrement d’ailleurs.

C’était juste pour faire remarquer que, pour les objets, le getter se faisait byref, donc pas forcément très safe pour l’objet renvoyé. Ce n’est pas forcément évident pour les débutants.
Donc faire un getter qui fait simplement return this.localObject(), ca sert à rien au niveau sécurité… stou :stuck_out_tongue:

Ha ok! j’ai cru que tu ralais que ca devrait pas etre comme ca… ouf j’ai eu peur :stuck_out_tongue: Oui, c’est un piege classique, on est tous tombe dedans a un moment ou a un autre :stuck_out_tongue:

[quote name=‹ GloP › date=’ 20 Nov 2004, 10:04’]Ha ok! j’ai cru que tu ralais que ca devrait pas etre comme ca… ouf j’ai eu peur :stuck_out_tongue: Oui, c’est un piege classique, on est tous tombe dedans a un moment ou a un autre :stuck_out_tongue:
[right][post=« 305166 »]<{POST_SNAPBACK}>[/post][/right][/quote]

Ben en fait, je préférerais juste que cela soit explicite, la notion de pointeur étant seulement sous jacente en Java.