mars
2006
Il ne s’agit peut être pas de la trouvaille du siècle mais elle est tout de même de taille : voici une astuce très propre permettant d’avoir plusieurs indexeurs dans une classe C#.
En effet, les indexers C# ont un problème : il ne peut y en avoir qu’un seul par classe et il ne porte pas de nom. Les habitués de langages comme Delphi (créé par le même concepteur que C#) sont généralement déçus car la notion d’indexeur est souvent présentée comme une grande nouveauté de C# alors qu’elle existe dans Delphi depuis des lustres mais en plus pratique : un « indexeur » est une propriété comme une autre, avec un nom, et on peut en avoir autant qu’on le désire dans une classe. Plus fort : l’un des indexeurs peut être noté « default » ce qui simule bien avant l’heure l’unique indexer non nommé de C#.
Donc voila la question : comment avoir plusieurs indexeurs dans une classe C# ?
Je ne parle pas d’un indexeur surchargé pour utiliser des index de types différents, mais bien de propriétés indexeurs différentes.
L’astuce est tirée d’un cours article anglais que vous pourrez lire ici dans sa forme originale.
Le principe est d’utiliser le fait qu’une classe peut supporter plusieurs Interfaces et qu’une interface peut définir un indexeur… En créant autant d’Interfaces que nécessaire il devient possible d’implémenter plusieurs indexeurs. Ce n’est pas aussi pratique que sous Delphi (où chaque indexer est nommé et s’implémente naturellement sans astuce) mais cela peut aider.
A noter que pour accéder à ces indexeurs supplémenteurs il faut transtyper l’instance par le nom de l’interface.
Pour comprendre voici l’exemple de code extrait de l’article de Namhrata Shah :
{
private int[] ArrayofStrings=new int[100];
public MyClass()
{
}
public int this[int index] /* Interface1 */
{
get
{
return ArrayofStrings[index];
}
set
{
ArrayofStrings[index]=value;
}
}
string Interface2.this[int index]
{
get
{
return ArrayofStrings[index].ToString() + " String";
}
}
}
public class MainClass
{
public static void Main()
{
MyClass o = new MyClass();
o[1] = 1; // interface1
Console.WriteLine(((Interface2)o)[1].ToString());
// Interface2
Console.ReadLine();
}
}
public interface Interface1
{
int this[int index]
{
get;
set;
}
}
public interface Interface2
{
string this[int index]
{
get;
}
}
Comme disait Garcimore « pas mal, pas mal… »
Et pour compléter, avec un indexeur on peut faire un « foreach », instruction très pratique, puissante, évitant les bugs dans les boucles etc…
Avec un getMachin(index), nada. que pouic. zero.
Tout à fait d’accord avec Merlin.
Soit ils retiraient totalement les indexeurs, soit ils ne les limitaient pas à être célibataire sans nom ni famille.
C’est quand même restrictif de supposer qu’une propriété sortira du lot par rapport aux autres.
Du coup, autant tout implémenter avec des collections et des Getters.
Il doit pourtant bien y avoir une raison objective… quid ?
à Laurent: le problème n’est pas qu’il n’y a pas de solution, heureusement. Avec des getMachins ça fonctionne.
Mais en toute honnêteté, je trouve franchement « cracra » qu’un tableau soit accédé par MonInstance[index] et l’autre par getTruc(index);
Deux écritures totalement différentes pour exactement le même besoin.
Cela manque d’unité et de cohérence.
Tu vas me dire, ben si les deux tableaux sont en getMachin, c’est cohérent…
Certes, mais si les indexeurs existent depuis toujours dans d’autres langages et s’ils existent aussi dans C# c’est parcequ’ils ont un intérêt.
De ce point vue la syntaxe Delphi par exemple est très supérieure en autorisant tout simplement autant d’indexeurs qu’on veut et en leur donnant un nom.. C’est tellement simple et évident…
Je ne pense pas que ce soit un si grosse lacune que ca.
A lui de chercher des bidouilles quelques peut limites, je vois pas le problème de faire :
MKQuery Builder { <br />
public Table getTable(int aIndex); <br />
public Jointure getJointure(int aIndex); <br />
} <br />
<br />
ou bien encore <br />
MKQuery Builder { <br />
public Tables getTables(); <br />
public Jointures getJointures(); <br />
} <br />
<br />
Bien sûr que cela a un intérêt ! Et de plus on voit mal en quoi cela briserait une quelconque règle de la POO.
Un simple exemple réel que je puiserais de mon expérience :
tu prends une classe Modèle de MK Query Builder, disons qu’elle reprénte une modèle de données, donc des tables et des jointures pour simplifier.
Cette classe _doit_ exposer deux listes indexées : Tables[index] et Jointures[index].
Je ne vois pas pourquoi 1) je devrais choisir laquelle des deux listes est la plus importante pour « mériter » d’être l’indexeur,elles sont d’importances fonctionnelles égales 2) je ne vois pas pourquoi l’une serait implémenté simplement par un indexeur et l’autre par un bricolage déclaratif moins pratique à utiliser.
C# pose ici deux problèmes : celui de n’avoir qu’un indexeur, celui d’interdire de nommer l’indexeur. Dans notre exemple Modele[index], selon le choix, sera donc une liste de tables ou de jointures. Ce qui implique de donner un sens plus fort à l’un ou l’autre. Et dans cet exemple c’est _sémantiquement_ une bêtise : un modèle sans table n’existe pas, mais sans jointure ce n’est plus un modèle de données, c’est juste une liste de tables…
Pour revenir à ta remarque sur ce qui serait « trop permissif » ou sur les « bonnes pratiques POO », il n’existe aucune règle POO qui interdirait qu’une classe possède et fédère plusieurs tableaux… Dès lors pourquoi en favoriser un lorsque plusieurs jouent, sémantiquement, un rôle aussi fort ?
Je maintiens que c’est une grosse lacune de C#. Il lui en faut bien quelques unes, rien n’est parfait
La question est : est-ce une bonne pratique « objet » de créer plusieurs indexeurs sur une classe ?
On reproche à certains langages d’être trop permissif, je pense que cette limitation du C# est volontaire.
J’ai un peu de mal à saisir l’interet (majeur ?) d’avoir plusieurs indexeurs sur une classe si ce n’est de garder certainnes habitudes hérités d’autres langages (en l’occurence delphi pour ton cas).
On ne va pas entrer dans la polémique des langages. J’ai dit « les habitués de langages comme Delphi » pour ne pas citer tous ceux qui lui ressemblent plus ou moins.
Pour ce qui est de C# et en restant dans le sujet, je suis moi aussi surpris que son créateur, père de Delphi (d’où la référence dans mon post), n’ait pas levé cette stupide limitation. J’attendais que C# 2.0 le fasse et il ne l’a pas fait. Bizarre, c’est pourtant très utile dans beaucoup de cas.
Y a pas que Delphi dans la vie…
Sur VB.NET toute propriété peut être indexable sans rien faire de spécial. J’ai jamais compris cette limitation du C# d’ailleur.
Sinon pour l’astuce c’est toujours bon a savoir…