Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de coq }

Actualités

var, var, var... marre, marre, marre...

Au risque de passer pour un vieux c** : pourquoi mettre du var partout ?
Je parle de l'utilisation avec autre chose que des types anonymes bien sûr.
Je parle de l'utilisation avec des vrais types, issus de la BCL ou non.

var str = "bla";
var i = 1;
var value = instance.Value;

On sent tout de suite la puissance du truc là, une vraie révolution.
On risque surtout de bien sentir passer la maintenance du code.

 

 

C'est énervant à la lecture

var i = 1;
var value = instance.Value;

Exercice : Au premier coup d'oeil, donner le type de "i".
Vous savez que le type sous un littéral de ce genre est int ? Et vos collègues ?

Exercice : Au premier coup d'oeil, donner le type de "value".
Là vous savez ? Recommencez la semaine prochaine.

 

C'est particulièrement énervant quand c'est utilisé dans des articles : si vous avez le malheur de lire l'article sans avoir sous la main votre documentation et/ou une connexion à internet, vous vous retrouvez avec une question bien sympathique : "mais c'est quoi le type sur lequel une méthode DoSomethingReallyCool est disponible ?"

var value = instance.Value;
...
value.DoSomethingReallyCool();


Ca m'est arrivé ce matin...

 

 

C'est risqué vis-à-vis des changements du code externe

En dehors du côté énervant à la lecture, un autre problème se pose : la détection des changements potentiellement critiques dans le code utilisé.

Imaginons que notre propriété Value retourne une valeur Int32.
Après récupération de cette valeur, nous la transmettons à un autre code/système qui pour une raison ou une autre ne la demande pas sous forme fortement typée Int32 mais plutôt Object, Byte[], ...

public class OtherClass
{
public void DoSomethingWith32Bits(Object value)
{
// ...
}
}

Nous pouvons utiliser au choix l'un de ces 2 codes (oui, je sais : ils sont bidons et moches mais ça suffira) : Method1 utilise un vrai type, Method2 laisse le compilateur inférer le type :

private void Method1()
{
OneClass instance = new OneClass();
OtherClass otherInstance = new OtherClass();
Int32 value = instance.Value;
otherInstance.DoSomethingWith32Bits(value);
}

private void Method2()
{
OneClass instance = new OneClass();
OtherClass otherInstance = new OtherClass();
var value = instance.Value;
otherInstance.DoSomethingWith32Bits(value);
}

Plus tard, le code de OneClass est changé : Value renvoie maintenant un Int64.
Oui, bien sûr, en théorie ce n'est pas censé arriver... Mais il y a la théorie et la pratique. Surtout si nous ne sommes pas maître du code de OneClass.

Avec le code typé normalement (Method1), notre ami le compilateur nous prévient à la compilation suivante : "Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?)"
Nous pouvons aller nous expliquer avec le propriétaire de OneClass, ou apporter les corrections nécessaires si c'était prévu.

Avec le code typé n'importe comment (Method2), nous venons de basculer automatiquement d'un entier 32 bits à un entier 64 bits.
Pratique ! J'espère juste pour nous que le code/système derrière OtherClass va lui aussi apprécier le changement, dans le cas contraire nous risquons quelques blagues particulièrement drôles :

  • Crash à la première exécution du code
    Peut être en prod si le code n'est pas couvert par des tests systématiques. Car si cette fonctionnalité n'est pas censée être impactée par l'opération de maintenance, elle ne sera peut être pas testée avant la mise en prod.
    • Soft : nous avons de la chance, c'est juste l'application.
    • Hard : OtherClass utilise en interne du code non managé et nous n'avons vraiment pas de chance, notre valeur est utilisée une fois l'exécution passée en kernel mode.
      Un bon BSOD nous fera bien voir par nos utilisateurs (ou plus si notre code est hébergé sur un système non dédié).
  • Nous nous apercevons au bout de plusieurs jours/semaines/mois que nous avons corrompus des données

 

On ne parle ici que d'un simple changement de type de valeur, mais avec l'utilisation de var il est de manière générale possible de basculer d'un type à un autre sans s'en rendre compte du moment que ce dernier expose les mêmes membres que ceux utilisés par le code spécifiant var comme "type".

 

Prenons le code suivant :

public class OneClass
{
public NiceClass Value
{
get
{
return new NiceClass();
}
}
}

public class NiceClass
{
public void DoSomethingNice()
{
Console.WriteLine("Hello !");
}
}

class Program
{
static void Main(string[] args)
{
OneClass instance = new OneClass();
var niceInstance = instance.Value;
niceInstance.DoSomethingNice();
}
}

Sortie : "Hello !"

Ajoutons le code de FakeNiceClass
public class FakeNiceClass
{
public void DoSomethingNice()
{
Console.WriteLine("BOOM !");
}
}

Et changeons juste le code de OneClass :
public class OneClass
{
//public NiceClass Value
//{
// get
// {
// return new NiceClass();
// }
//}

public FakeNiceClass Value
{
get
{
return new FakeNiceClass();
}
}
}

Ca compile sans problème, et la sortie est "BOOM !".

La version de Main avec vrai typage aurait nécessité, pour passer la phase de compilation, de mettre en place un opérateur de conversion implicite :

  • sur FakeNiceClass, et c'est quand même le code de NiceClass.DoSomethingNice qui aurait été exécuté, pas celui de FakeNiceClass.DoSomethingNice.
  • sur NiceClass, et c'est le code de NiceClass.DoSomethingNice qui aurait été exécuté, sauf décision explicite dans NiceClass.DoSomethingNice de déléguer l'exécution à FakeNiceClass.DoSomethingNice persistée lors de la conversion.

Déjà plus compliqué à réussir de manière non intentionnelle...

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: samedi 10 octobre 2009 22:02 par coq
Classé sous : ,

Commentaires

richardc a dit :

Comment dire ? JE SUIS D'ACCORD A 15000%

J'avais fait la remarque plusieurs fois et effectivement, on m'avait "traité" de vieux con (enfin j'exagère. On m'avait juste dit que je comprenais rien).

var == type anonyme ou alors, quand on est fainéant et qu'on a des requêtes Linq complexes et moralité, on sait plus trop ce qu'il retourne (au pire, on met un IEnumarable

<T>).

Le pire c'est en VB .NET car là, on comprend encore moins avec les paramètres par défaut "préconisés" par MS.

Développeurs VB, quand vous commencez un Projet, Option Explicit = On et Option Infer = off.

# octobre 11, 2009 11:31

VANNESTE Xavier a dit :

Ayant connu l'enfer des variants sous VB6 je suis tout à fait d'accord "un vieux con de plus", même si ce n'est pas tout à fait la même chose car les variants pouvez en plus changer de type au cours de l'execution. Il est clair que les var donnent une certaine illisibilité au code. Les erreurs à la compilations "dites de syntaxe" sont les plus simples à trouver, transformer une erreur de syntaxe en erreur logique "le type dépend du contexte et plus du choix du développeur" en utilisant les var est vraiment une aberration.

# octobre 11, 2009 13:19

abolibibelot a dit :

On peut abuser de var, mais son usage modéré rend le code lisible et évite de tout répéter quinze fois (bon, une fois).

Utiliser un var pour typer une variable qui reçoit le retour d'une méthode est plus discutable si le nom de la méthode n'est pas suffisamment explicite.

# octobre 11, 2009 18:25

Graveen a dit :

on dira ce qu'on voudra mais l'inférence de type c'est pas trés pro. Je concois que lorsqu'il n'y a aucune ambiguité (et je pense à une requete LinQ) cela puisse etre utilisé, mais sinon c'est une habitude à perdre de toute urgence: ca n'amène rien sinon moins de clarté dans son code, et les effets de bords peuvent être désastreux.

# octobre 11, 2009 19:26

DjoDjo a dit :

je suis encore trop jeune pour être un vieux con mais je suis tout à fait d'accord.

Si vous voulez être fainéant et que vous avez Resharper, une fois votre ligne de code terminée, de grace, demandez à Resharper de reformater la ligne pour mettre le vrai type... (et non ca ne demande pas une manipulation extraordaire)

# octobre 12, 2009 09:22

Jem a dit :

Je comprends tout à fait les arguments, mais n'empêche que c'est très énervant de devoir taper des types de façon redondantes comme par exemple :

Dim drMouv_detail As AgrifactTracaDataSet.Mouvement_DetailRow = Cast(Of AgrifactTracaDataSet.Mouvement_DetailRow)(Cast(Of DataRowView)(Me.MouvMPBindingSource.Current).Row)

Ca fait des lignes sans fin qui n'aident pas non plus à la lisibilité, voire qui pourraient inciter à faire du Set Strict Off.

# octobre 12, 2009 09:25

KooKiz a dit :

Je n'utilise var que pour les fonctions renvoyant un objet "complexe" (comprendre : pas un type de base comme int/string/char/float/... peu importe la manière dont il est implémenté).

Typiquement :

var order = GetOrder();

order.Delete();

Dans le cas présent, pourquoi le développeur aurait besoin de savoir que GetOrder renvoie un CoqBlog.Order, un CancelledOrder, ou un IOrderableThing ? L'essentiel est ici l'information fonctionnelle : il s'agit d'une commande, et on va la supprimer. Le code gagne de fait en lisibilité.

Par contre effectivement quand je vois :

var i = 2;

J'ai envie de donner des baffes.

# octobre 12, 2009 09:30

Laurent GEFFROY a dit :

Personnellement, j'autorise le var que dans un seul cas :

var myObj = new MyCustomObject();

plutôt que

MyCustomObject myObj = new MyCustomObject();

Et dans aucun autre. Au moins, à la lecture, on voit bien que c'est un MyCustomObject et rien d'autre.

# octobre 12, 2009 09:58

Jb Evain a dit :

«on dira ce qu'on voudra mais l'inférence de type c'est pas trés pro.»

Bah oui tiens, on va dire ça aux professionnels qui écrivent des grosses applications en F#, OCaml, Haskell, Scala, et j'en passe :)

# octobre 12, 2009 10:31

davidyannick a dit :

je me joins au club des "vieux cons" ;)

la relecture est un calvaire

# octobre 12, 2009 10:42

Erebuss a dit :

Ca permet de rapidement détecter ceux qui utiliser Resharper aussi ;)

# octobre 12, 2009 10:42

Graveen a dit :

je ne connais pas ces languages précisement, mais je suppose que le paradigme et/ou la syntaxe permettent une utilisation judicieuse de l'inférence :D

blague à part, c'est surtout l'utilisation systématique et pour des raisons discutables qui est dommage. :)

# octobre 12, 2009 10:45

RaptorXP a dit :

Meme experience, apres avoir fait remarquer à des collègues que leur sur-utilisation de var rendait le code illisible, je me suis limite fait traité de vieux con, et on m'a entre autre expliqué que c'est que je ne comprenais rien à l'inférence de type.

Au final, mes arguments ont quand même prévalus, et j'ai réussi a faire bannir le var, à part dans le cas décrit par Laurent GEFFROY au dessus.

Je tiens resharper coupable (et ceux qui lui obéissent sans réfléchir) pour cette tendance à abuser de var.

# octobre 12, 2009 11:18

DjoDjo a dit :

J'utilise Resharper et ce genre de chose est paramétrable, le mien me propose de changer les var par le vrai type.

# octobre 12, 2009 12:56

ebartsoft a dit :

Vieux con +1 !

Quoi que... tant qu'a faire du scripting et de la virtual machine autant ne garder qu'un type "VAR". Au moins on ne risque pas de se tromper !

haha

# octobre 12, 2009 17:10

VANNESTE Xavier a dit :

@Laurent GEFFROY: je suis mitiger pour l'utilisation de var myObj = new MyCustomObject(), car en cas de necessité de polymorphisme, par exemple utilisation de la classe de base ou d'un interface (exemple design pattern stratégie ou décorateur) cette utilisation rend tout d'une illisibilité totale.

Donc le var c'est tabou on en viendra tous a bout :)

# octobre 12, 2009 21:09

mchouteau a dit :

Pour ma part je ne vois que des avantages a utiliser l'inference de type, on peut rechigner sur certains points, mais c'est tellement pratique, et puis ça nous prépare à "dynamic" ;)

# octobre 12, 2009 21:21

coq a dit :

Ha oui mais non, je préfère prendre le côté aventure cette fois-ci et le découvrir sans préparation préalable ! ;)

# octobre 12, 2009 21:53

abolibibelot a dit :

@Xavier

"je suis mitigé pour l'utilisation de var myObj = new MyCustomObject(), car en cas de nécessité de polymorphisme, par exemple utilisation de la classe de base ou d'un interface (exemple design pattern stratégie ou décorateur) cette utilisation rend tout d'une illisibilité totale."

Là je pige plus du tout, on doit pas avoir la même définition du polymorphisme.

Qu'est-ce qui t'empêche d'écrire

ISeeDeadPeople obj = new MyCustomObject(); si tu veux typer dans l'interface ? et

var obj = new MyCustomObject(); si tu n'en a rien à frak ?

Là où je comprends toujours pas ce que euh si tu passes ton instance à une méthode qui attend un ISeeDeadPeople, la magie du polymorphisme fait que ça marchera de toute façon en passant un CustomObject.

Bref, ça fait penser aux arguments qui disent que getTruc() et setTruc() c'est mieux que la propriété Truc parce que la propriété cache le fait qu'il s'agit de méthodes.

# octobre 13, 2009 10:36

VANNESTE Xavier a dit :

@abolibibelot

je n'ai pas dis que ce n'etais pas possible, j'ai juste pointer du doigt l'illisibilité du code.

C'est pas parce que c'est nouveau que c'est mieux que l'ancien.

Et j'ai pas non plus compris l'allusion a gettruc et settruc.

# octobre 13, 2009 12:22

romain verdier a dit :

Ceci est un semi-troll :

"Ceux qui codent réellement savent qu'utiliser var ne pose aucun problème."

# octobre 13, 2009 12:31

abolibibelot a dit :

@Xavier

Ce qui compte c'est l'intention suggérée par ton code par le nom des méthodes et propriétés, pas les béquilles de syntaxe.

Bizarrement, je n'ai aucun problème pour lire du code écrit en Python ou Ruby (ou F#) et pourtant le typage est implicite et - horreur - dynamique. En F#, on ne déclare le type que si l'on doit absolument le faire.

Je ne vois pas en quoi le pléonasme est un principe de programmation.

# octobre 13, 2009 12:45

proviste a dit :

Eternel débat des raccourcis de codage qui ne nuisent qu'à la lisibilité.

Eternel débat de tout ce qui nuit à la lisibilité au profit de... l'orgueil du développeur.

# octobre 13, 2009 17:35

KooKiz a dit :

Je suis récemment tombé sur un billet d'un blog décrivant les 10 phases d'adoption d'une nouvelle fonctionnalité au niveau d'un langage. En partant de la phase "à quoi ça sert ?" et "c'était mieux avant", puis "utilisation à tord et à travers", pour finalement devenir une fonctionnalité "normale" du langage, utilisée seulement quand il le faut (comme les generics aujourd'hui).

Hélas j'étais tombé sur ce blog vraiment par hasard en surfant de lien en lien, et je n'arrive pas à remettre la main dessus :(

# octobre 13, 2009 17:44

Mitsufu a dit :

Débat intéressant, en tout cas, peu importe que l'on soit vieux ou con...

Je suis POUR le var.

L'exemple montré par Gaël ne me choque pas du tout.

Je comprends que cela perturbe, mais vous êtes tous plus ou moins en train de dire que les dévs F# sont des fous...ce qui à mon avis est loin d'être le cas.

De même que l'injection de dépendance et les classes factory tentent à faire disparaitre la visibilité du type réel de l'instance, le mot clé var est un fabuleux moyen de ne plus se soucier du type.

La vrai question est: a-t-on vraiment besoin de connaitre le type exact pour comprendre un code ?

Je ne pense pas.

La lecture "fonctionnelle" offerte par var permet d'avoir une approche différente sur le code et comme dans l'exemple de Gaël encore une fois, c'est pour moi un avantage que le code continue de fonctionner après changement du type de 'value'.

Personnellement je mets var presque partout et cela ne me pose aucun problème.

Mitsu

# octobre 18, 2009 15:32

coq a dit :

Oula, je précise : je ne dis pas que les dévs qui utilisent F# sont des fous, je n'ai pas vraiment regardé ce langage donc je peux difficilement donner un avis dessus :-)

Mais c'est sûr que si un des aspects forts est de ne jamais vraiment savoir ce qu'on manipule, il y a peu de chances que j'accroche.

# octobre 19, 2009 20:07

coq a dit :

# novembre 1, 2009 10:14

KooKiz a dit :

C'est bien celui-là, merci. C'est 7 phases et non 10, ça doit être pour ça que je n'arrivais pas à remettre la main dessus :p

# novembre 1, 2009 10:31

coq a dit :

C'est ce que je me suis dit en le voyant ;-)

# novembre 1, 2009 10:51
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01