Le blues du "Set Of" de Delphi en C#

Il y a bien fort peu de chose qui puisse faire regretter un delphiste d'être passé à C#, et quand je parle de regrets, c'est un mot bien fort, disons plus modestement des manques agaçants. 

Rien qui puisse faire réellement pencher la balance au point de revenir à Delphi, non, ce langage est bel et bien mort, assassiné par Borland/Inprise/Borland/CodeGear et son dernier big boss, tod nielsen qui ne mérite pas même les majuscules à son nom mais là n'est pas la question.

Donc il existe syntaxiquement trois choses qui peuvent agacer le delphiste, même, comme moi, quand cela fait au moins cinq ans maintenant que je ne pratique presque plus que C# (et un peu de Delphi pour maintenir des vieilleries et aider certains clients à tourner la page en migrant sous C#).

La première qui revient à longueur de code, ce sont ces satanés parenthèses dans les "if". On n'arrête pas de revenir en arrière parce qu'on rajoute un test dans le "if" et qu'il faut remettre tout ça entre de nouvelles parenthèses qui repartent depuis le début. Certes on gagne l'économie du "then" pascalien, mais que ces parenthèses du "if" sont épouvantables et ralentissent la frappe bien plus qu'un "then" unique et ponctuel qu'on ne touche plus une fois écrit même si on rajoute "and machin=truc" ! A cela pas d'astuce ni truc. Et aucun espoir que les parenthèses de C# dans les "if" soient abandonnées un jour pour revenir au "then" ... Donc faut faire avec ! Le dogme "java/C++" est bien trop fort (quoi que C# possède le Goto de Delphi, ce qui n'est pas la meilleure idée du langage :-) ).

La seconde tracacerie syntaxique est cette limitation totalement déroutante des indexeurs : un seul par classe et il ne porte pas de nom. this[], un point c'est tout. Je sais pas ce que notre bon Hejlsberg avait en tête, mais pourquoi diable n'a-t-il repris de Delphi qu'un seul indexeur et non pas la feature entière ? Il fait beaucoup de recherche, et le fait bien, mais je présume qu'il n'a jamais plus codé un "vraie" appli depuis des lustres... Car dans la vraie vie, il existe plein de situations où un objet est composé de plus d'une collections. Une voiture a 4 roues et 2 ou 4 portières. Pourquoi lorsque je modélise la classe Voiture je devrais donner plus d'importance aux roues qu'aux portières et choisir lesquelles auront le droit d'être l'unique indexeur de la classe ? Pourquoi tout simplement ne pas avoir Voiture.Roues[xx] et Voiture.Portières[yy] ? Mon incompréhension de ce choix très gênant dans plus d'un cas a été et reste des années après totale. Surtout après toutes les évolutions de C# sans que jamais cette erreur de conception ne soit corrigée. Pas suffisant pour faire oublier toute la puissance de C# et le bonheur qu'on a à travailler dans ce langage, mais quand même, ça agace.

Enfin, dans la même veine d'ailleurs, l'absence de "Set of" est cruelle. Pourquoi avec zappé cette feature de Delphi dans C# alors que bien d'autres ont été reprises (avec raison ou moins comme le Goto) ?

Mais là on peut trouver des astuces et certains (dont je fais partie) ont écrit ou essayer d'écrire des classes "SetOf" qui permettent de gérer des ensembles comme Delphi, mais que c'est lourd tout ce code au lieu d'écrire "variable machin : set of typeTruc" !

Autre astuce moins connue, et c'est pour ça que je vous la livre, est l'utilisation fine des Enums. En effet, tout comme sous Delphi, il est possible d'affecter des valeurs entières à chaque entrée d'une énumération. On peut donc déclarer "enum toto { item1 = 5, item2 = 28, item3 = 77 }". 

Mais ce que l'on sait moins c'est que rien ne vous interdit d'utiliser des puissances de 2 explicitement car les Enums savent d'une part parser des chaînes séparées par des virgules et d'autre part savent reconnaître les valeurs entières cumulées.

Ainsi, avec enum Colors { rouge=1, vert=2, bleu=4, jaune=8 }; on peut déclarer :
Colors orange = (Colors)Enum.Parse(typeof(Colors),"rouge,jaune"); // étonnant non ?

La valeur de "orange" sera 9 qu'on pourra décomposer facilement, même par un simple Convert.ToInt64.

Pour ne pas réinventer la roue et éviter de faire des coquilles je reprends cet exemple tel que fourni dans la doc MS. Voici le code complet qui vous permettra, peut-être, d'oublier plus facilement "Set of" de Dephi...

Stay tuned!

Code de la doc MS :

using System;

public class ParseTest
{
    [FlagsAttribute]
    enum Colors { Red = 1, Green = 2, Blue = 4, Yellow = 8 };

    public static void Main()
    {
        Console.WriteLine("The entries of the Colors Enum are:");
        foreach (string colorName in Enum.GetNames(typeof(Colors)))
        {
            Console.WriteLine("{0}={1}", colorName,
                                         Convert.ToInt32(Enum.Parse(typeof(Colors), colorName)));
        }
        Console.WriteLine();
        Colors myOrange = (Colors)Enum.Parse(typeof(Colors), "Red, Yellow");
        Console.WriteLine("The myOrange value {1} has the combined entries of {0}",
                           myOrange, Convert.ToInt64(myOrange));
    }
}

/*
This code example produces the following results:

The entries of the Colors Enum are:
Red=1
Green=2
Blue=4
Yellow=8

The myOrange value 9 has the combined entries of Red, Yellow

*/

 

 


Tags: , Categories: Astuce | C# | Delphi | Humeur

Thu 08 May 2008 09:56 7 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Comments

May 8. 2008 13:19

Hugues Van Landeghem

tu as oublié Embarcadero Technologies !

Borland/Inprise/Borland/CodeGear/Embarcadero Technologies...

Hugues Van Landeghem

May 8. 2008 15:13

Romain

J'ai toujours considéré que Delphi était un très bon langage et c'est aussi pour ça que j'ai aimé C# lorsque je l'ai découvert. Par contre, ce dernier est pour ma part en tout point supérieur à Delphi, y compris donc lorsqu'on parle de la concision du if.

Sinon, j'ai quelques remarques, même s'il est improbable que cela fasse disparaitre ta frustration Smile

- On peut avoir plusieurs indexers ayant des types de clés différents dans une même classe :

public string this[int index]
{
    get { return string.Empty; }
}

public string this[string key]
{
    get { return string.Empty; }
}

- Pourquoi les nommer ? Dans l'exemple que tu proposes avec Voiture.Roues[xx] et Voiture.Portières[yy], je ne comprends pas vraiment pourquoi deux propriétés Roues et Portières exposées au niveau de ta classe Voiture ne suffiraient pas... A vrai dire, c'est même la modélisation la plus naturelle qui soit. Une voiture n'est pas une collection indexable. Par contre, elle peut s'appuyer sur des conteneurs pour gérer sa composition.

- Le goto ne vient pas de Delphi... Il doit s'agit de la première instruction de contrôle en programmation.

- Bon ok, les sets en Delphi c'était bien cool. Et la solution que tu proposes à un coût : toutes les méthodes statiques de la classe Enum utilisent la reflexion...

Romain

May 9. 2008 13:45

Sylvain

Nostalgie quand tu nous tiens Smile
C'est vrai, tiens, pour les indexers, j'avais pas remarqué.
Quand au tip sur l'enum, très classe j'adore !

Sylvain

May 10. 2008 14:36

Cyril

Je trouve ton retour d'ancien delphiste très interessant.

Je connais pas delphi, mais je ne suis pas sur que l'absence d'indexeur multiple soit un réél problème. Dans la plupart des cas, une propriété de type Array suffira largement, tu pourras avoir un

voiture.Roues[0] // ou Roues est une propriété de type Roue[]

Si tu veux mettre autre chose qu'un entier en index, par exemple :

voiture.Roues[Arriere | Droite] //ou Arriere | Droite est une valeur d'enum

Tu peux alors créer une classe RoueIndex possèdant un indexeur, puis ensuite exposer une propriété de ce type au niveau de la casse voiture. Certes c'est un peu moins simple que ce que propose Delphi (d'après ce que j'ai compris) mais je ne trouve pas ça dérangeant.

PS : on peut avoir plusieurs indexeurs différents, ils se différencient par le type de l'index items["pouet"] ou items[0]

Cyril

May 12. 2008 08:08

Olivier

A Romain:
C#, comme je le dis en intro, est bien entendu largement supérieur à Delphi et même à Java ou C++. Mais la perfection n'étant pas de ce monde, je trouve que C#, notamment dans le if avec toutes les paranthèses obligatoires rend les choses moins rapides à écrire et moins faciles à lire. "if a or b then x" me semble bien plus simple à lire et surtout plus rapide à écrire que if (a || b) {x...}. Le double pipe (altGR) n'est pas fait pour le clavier français, ne parlons pas des { } (AltGR aussi). C# est trop "QWERTY", c'est un petit reproche, les américains, c'est une facheuse habitude, ne pensent pas assez aux autres claviers où tous ces symboles demande une gymnastique des doigts. C'est là que j'apprécie d'être pianiste mais je vois des tas de gens frapper en faisant des contorsions des deux mains pour, tous les deux caractères, avoir les ALTGr-machin qui sont indispensables en C#.

Mais cela reste un reproche limité on est bien d'accord.

Concernant les indexeurs là c'est un vrai reproche. Ta solution qui consiste à jouer sur le type de l'index ne répond pas à la question en fait. Elle amène de la confusion si this[string] renvoie les roues de la voiture et this[int] renvoie les portières (c'est un exemple). Mais cela n'est pas possible (this[] n'a qu'un seul type), à moins que this[] ne retourne un type object. Mais on perd alors le typage fort et les contrôles qui vont avec, ce qui n'est pas une bonne idée (sans oublier le code à ajouter pour transtype le résultat à chaque fois).
Malheureusement cette astuce ne répond pas au besoin d'avoir plusieurs propriétés indexées différentes dans une classe.
Et je crois que justement tu m'as mal compris quand tu dis pourquoi ne pas avoir deux propriétés roues et portières dans Voiture. C'est justement ce que je veux, des propriétés nommées mais indexées  this[] existe, pourquoi ne pas permettre comme dans Delphi d'en avoir plusieurs et avec un nom. Là est la question.

Le Goto... certes il vient de l'assembleur et même d'avant, c'est la base de la prise de décision et donc de la notion de programme. Mais Delphi était un langage morderne, aucunement accroché à la norme Pascal (qui n'existe pas) aucune obligation de conserver cet avatar de la programmation des années 70. Et si je parle du Goto de Delphi dans C#, c'est justement parce ce que les deux langages ont été créés par la même personne et que je trouve cela étonnant, exactement comme les amis du Goto que sont les labels identiques en C# à Delphi. Mais bon cela n'est pas gênant il suffit de ne pas s'en servir  

Pour finir avec les "Set of", la réflexion est certes couteuse, mais je doute qu'une application ne soit sérieusement ralentie par son utilisation. Quand je dis "sérieusement ralentie" je veux dire passer d'une bombe à une appli qui rame. Faut pas exagérer. C'est comme string et StringBuilder, j'ai fait des tests sur la question et entre l'un et l'autre le gain de temps est soit invisible, soit inverse à ce qu'on attend... Il ne faut donc pas trop s'inquiéter. Certes une appli basée toutes les 2 instructions sur la réflexion serait moins véloce, mais cela serait-il même visible pour l'utilisateur ? J'en doute fort, expérience et tests effectués.

Il ne faut donc pas se priver d'utiliser ce qui améliore le code, la lisibilité et sa maintenabilité. Une appli moyennement rapide mais maintenable vaut professionnellement mille fois la même appli plus rapide mais chargée d'un code difficile à lire et à maintenir. C'est la règle d'or à avoir en tête pour choisir une implémentation plus qu'une autre, en tout cas professionnellement (et en dehors de certains traitements où la milliseconde serait critique, ce qui est rare).

Olivier

May 12. 2008 08:11

Olivier

A Hughes:

La vache j'avais pas encore vu ça Smile

Alors il faudra écrire Borland/Inprise/Borland/CodeGean/Embarcadero Technologies !

Le jeu de piste continue, l'embrouille des utilisateurs aussi. Comment peut-on aligner autant d'erreurs...

Olivier

May 12. 2008 08:17

Olivier

A Cyril:

Hélas une propriété de type Array n'est pas une propriété indexée comme this[].
Dans une propriété tu as un getter et un setter qui te permettent de contrôler l'accès à chaque élément. Avec une Array, si le programme utilisateur modifie l'élément 3 la classe de base ne le saura jamais par exemple... Alors que si on passe par un setter, la classe peut déclencher des traitements et des contrôles.

Ce qui se rapprocherait le plus d'une propriété indexée, c'est de publier une propriété de type collection générique. Mais cela réclame plus de code. Et puis la question revient : pourquoi diable se limiter à this[] alors que le procédé peut être étendu à toute propriété indexée comme cela existe en Delphi, créé par la même personne (c'est pour cela que je vais tous ces rapprochements d'ailleurs).

Quant au fait que this[] accèpte plusieurs index différents, cela est pratique dans certains cas, mais ne répond pas au besoin car this[] ne peut retourner qu'un seul type et là il faut choisir: les roues ou les portières ? ...

Olivier

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading