J’ai une petite question pour tous les cracks de .Net.
En lisant le code du projet sur lequel je bosse, je suis tombé sur plein de méthodes (la plupart dans le BL) qui utilisent un passage de paramètre par référence (le mot-clef ref quoi) pour des types class.
En regardant l’intérieur des méthodes je ne voyais à aucun endroit un nouvel objet créé et passé au paramètre.
Trouvant cela bizarre et pouvant porter à confusion, je vais poser la question à la personne responsable qui m’explique que cela permet de savoir que l’objet est modifié à l’intérieur de la méthode et que le tool UML qu’il utilise fait ça lorsqu’on met le paramètre en mode “in out”.
Soit, mais je trouve quand même cela plus qu’étrange et potentiellement dangereux (on risque de drôles de trucs s’il reste des variables qui pointent sur l’ancienne référence par exemple). Pour moi ref n’a absolument rien à voir avec un potentiel mot-clé “in out” tel qu’on le trouve dans certains langages (euh… enfin dans ADA quoi :)).
Les classes de toute facon sont toujours passées en ref non ? c’est sur les struct que ca devient important, utile, et en plus, ca permet de gagner en perf, puisque ca evite de creer un nouvel objet a chaque appel (pour les structs, pour les classe, c’est toujours en ref).
Edit: Ha oui, ok, dans ce sens la, le mot ref permet de specifier la possibilité de changer l’objet dans la fonction par un nouvel objet. Dans ce cas, oui, ca peut etre dangereux.
Justement, il y a une petite subtilité!
Si tu as un type référence (une classe donc) et que tu utilises le mot-clef ref, c’est « la variable » qui est passée en référence.
En gros tu as le droit de redéfinir l’objet vers lequel « pointe » la variable qui est passée comme paramètre de ta méthode.
Essaye le petit exemple de Styx avec et sans le mot-clef ref. Sans, p n’est pas modifié, tu te retrouves avec un affichage « Jacques », avec tu as un affichage « Michel ».
Que ref permet d’assurer la non copie d’un objet en passage de paramètre, que programmer est de toute façon dangereux quand on fait n’importe quoi, que la projection de inout vers ref est pas complètement stupide, puisque c’est le rôle de ref.
Enfin, je ne crois pas qu’on puisse avoir des variables associées à ref. Donc pas de problème d’ “ancienne référence” (à moins que je n’ai pas compris de quoi tu voulais parler).
Ca n’a absoluement aucun interet dans 99% des cas. En tout cas c’est puissament incomprehensible de faire ca par defaut. L’interet principal c’est avec les value types pour eviter les copies de partout et trop d’objects, et quelques rares cas bien particulier sur les reference type. Le faire par defaut, c’est moche, pas pratique, ca rend le code lourd, ca encourage les side effects indesirables et incomprhensibles, et ca n’apporte strictement rien si ce n’est plus de bug, plus de problemes de maintenance et plus de prise de tete. Ca viendrait a l’idee a personne en C ou C++ de rajouter un (void*) devant chacun des parametres de sa fonction juste “parceque”, par defaut un parametre, c’est un parametre, pas un retour dans un appel de fonction… Rajouter un niveau d’indirection quand on en a pas besoin c’est vraiment aimer se tirer une balle dans le pied.
Merci Glop, ça me conforte exactement dans mon idée… enfin, c’est pas le premier truc sur lequel je tombe du genre… et je doute que ce soit le dernier.
[code]public static void RunSnippet()
{
Person p = new Person(« Jacques »);
Person p2 = p;
Console.WriteLine(« P : {0}; P2 : {1} », p, p2);
SetPerson(ref p);
Console.WriteLine(« P : {0}; P2 : {1} », p, p2);
}
static void SetPerson(ref Person person)
{
person = new Person(« Michel »);
}
class Person
{
public Person(string name)
{
Name = name;
}
public string Name;
public override string ToString()
{
return Name;
}
}[/code]
On aura comme sortie :
P : Jacques; P2 : Jacques
P : Michel; P2 : Jacques
alors que, de base on s’attendrait plutôt à une sortie du genre :
P : Jacques; P2 : Jacques
P : Michel; P2 : Michel
Personnellement je trouve que ça peut apporter beaucoup de confusion car on ne peut savoir si l’on va se retrouver dans le premier cas ou le second qu’en regardant le code de la méthode qu’on utilise. Du coup on est quasiment obligé de partir du principe que l’on va se retrouver dans la première situation et vérifier que toutes les variables qui pointent sur cet objet soient mises à jour ou mises à null si elles sont inutiles, et ça peut devenir problématique si l’objet est référencé dans des structures plus complexes.
Sinon, j’ai essayé de réfléchir à un cas concret d’utilisation du mot-clé ref avec un type référence… et je dois dire que je ne vois pas. Pour les types valeurs je vois bien l’avantage, mais pour les types référence, je vois vraiment pas de cas concret. Quelqu’un a une idée?
[quote=“Gimly, post:7, topic: 47990”]On aura comme sortie :
P : Jacques; P2 : Jacques
P : Michel; P2 : Jacques
alors que, de base on s’attendrait plutôt à une sortie du genre :
P : Jacques; P2 : Jacques
P : Michel; P2 : Michel[/quote]
Je connais pas vraiment le C#, mais en regardant le bout de code (avec mes connaissances en C/C++ et java) c’est bien la 1ère solution qui me parait logique, vu que dans ton setPerson tu fais un new, c’est une nouvelle instance, donc p2 n’est plus sur le même objet que p.
Oui, sauf que si à la place du person = new Person(« Michel ») dans la méthode on avait simplement fait person.Name = « Michel », on se serait retrouvé dans la situation suivante :
P : Michel; P2 : Michel
Donc, imagine que tu n’aies pas accès au code fonctionnel de la méthode, que le mot-clef ref est utilisé pour passer le paramètre et que tu ne sais pas si à l’intérieur de la méthode il y a un person = new Person(« toto ») ou si c’est simplement un person.Name = « toto »…
Tu te retrouves largement embêté.
Après c’est clair que dans l’exemple, il est tout à fait logique de se retrouver dans la situation 1). Ce que je voulais dire c’est que quand tu utilises une méthode du genre (dans un cas concret elle s’appellerai SetName par exemple) tu t’attends pas tellement à avoir le genre de surprises que peut amener ce genre d’assignations.
Et je voudrais aussi préciser que dans le cas où une assignation est faite dans une méthode ou le param n’est pas passé avec le mot-clef ref, l’assignation est valide mais seulement pour le scope de la méthode. Dans le cas de l’exemple au dessus, si l’on enlève « ref », on va avoir comme résultat :
P:Jacques; P2:Jacques
P:Jacques; P2:Jacques
[quote=« Gimly, post:10, topic: 47990 »]Oui, sauf que si à la place du person = new Person(« Michel ») dans la méthode on avait simplement fait person.Name = « Michel », on se serait retrouvé dans la situation suivante :
P : Michel; P2 : Michel
Donc, imagine que tu n’aies pas accès au code fonctionnel de la méthode, que le mot-clef ref est utilisé pour passer le paramètre et que tu ne sais pas si à l’intérieur de la méthode il y a un person = new Person(« toto ») ou si c’est simplement un person.Name = « toto »…
Tu te retrouves largement embêté.[/quote]
Ca me viendrait pas à l’idée de faire un new dans un set, et je pense pas que ça soit très courant ni même acceptable, non ?
[quote=« Gimly, post:10, topic: 47990 »]Et je voudrais aussi préciser que dans le cas où une assignation est faite dans une méthode ou le param n’est pas passé avec le mot-clef ref, l’assignation est valide mais seulement pour le scope de la méthode. Dans le cas de l’exemple au dessus, si l’on enlève « ref », on va avoir comme résultat :
P:Jacques; P2:Jacques
P:Jacques; P2:Jacques[/quote]
Oui si tu passes pas par référence p ne sera pas modifé, c’est logique
Oui, d’où ma question qui était de savoir s’il existait un cas pratique où utiliser le mot-clef ref avec un type référence (et donc faire un « new » dans un set (uhuh, suupra clair)) était acceptable, voir utile.
Pour le moment je vois ça comme le goto, un truc qui a été gardé plus pour une raison historique qu’autre chose et qu’il est bon de connaitre pour savoir ce que ça fait, mais qu’il ne faut pas utiliser.
Il y a des tas de cas d’utilisation do goto pour sortir de boucles imbriquees quand il faudrait faire deux break d’affiles qui sont parfaitement valides. Et des cas d’utilisation de ref pareil, on peut pas faire une regle qui dit “il faut pas”, ca fait parti de la boite a outil et c’est pas un des outil les plus utilise c’est tout :).
[quote=« GloP, post:17, topic: 47990 »]Sortir d’une double loop imbriquee par exemple dans certains cas.
[code]for (bla) {
for(bla2) {
if(haaaaaaa sortir on va pas tout faire quand meme)
goto exit;
if(haaaaaaa sortir mais pas completement)
continue;
}
}
exit:
la suite...[/code][/quote]
Je voulais faire un exemple identique … et montrer aussi qu’en IL c’est identique à d’autres instructions mais le compilo optimise tellement bien qu’avec un exemple trop simpliste, il ne reste plus rien à montrer
[code] static void Main(string[] args)
{
int i = 1;
while (true)
{
i++;
if(i==2)break;
}
while (true)
{
i++;
if(i==3) goto here;
}
here:;
}[/code]
après reflectorisation :
private static void Main(string[] args)
{
bool CS$4$0000;
int i = 1;
do
{
CS$4$0000 = true;
i++;
}
while (i != 2);
do
{
CS$4$0000 = true;
i++;
}
while (i != 3);
}
C’est tout pareil :crying:
"Hummm tu aimes mettre des goto … non ce n’est pas sale … "
Par contre on remarquera qu’il crée quand même le booléen.
GloP tu as une idée du pourquoi du comment pour le booléen ?