L'interface IComparable est-elle obsolète / «nuisible»?

11

IComparable ne fonctionne que dans un sens

Disons que vous avez une Employeeclasse. Dans une vue, vous voulez tout afficher Employeestrié par nom - dans une autre, par adresse. Comment allez-vous y parvenir? Pas avec IComparable, du moins pas de façon idiomatique.

IComparable a la logique au mauvais endroit

L'interface est utilisée en appelant .Sort(). Dans une vue montrant Customertrié par nom, aucun code n'implique comment il va être trié.
D'un autre côté, la Customerclasse suppose comment il va être utilisé - dans ce cas, il sera utilisé dans une liste triée par noms.

IComparable est utilisé implicitement

En comparaison avec les alternatives, il est très difficile de voir où la logique de comparaison est utilisée - ou pas du tout. En supposant votre IDE standard et à partir de la Customerclasse, je devrai

  1. Rechercher toutes les références à Customer
  2. Trouvez les références utilisées dans une liste
  3. Vérifiez si ces listes ont déjà fait .Sort()appel à elles

Ce qui est probablement pire, si vous supprimez une IComparableimplémentation qui est toujours utilisée, vous n'obtenez aucune erreur ou avertissement. La seule chose que vous obtiendrez est un mauvais comportement dans tous les endroits qui étaient trop obscurs pour que vous y pensiez.

Ces problèmes combinés, ainsi que l'évolution des exigences

La raison même pour laquelle j'ai pensé à cela, c'est parce que ça a mal tourné pour moi. J'utilise heureusement IComparablemon application depuis 2 ans maintenant. Maintenant, les exigences ont changé et la chose doit être triée de 2 manières différentes. Il a remarqué qu'il n'est pas amusant de suivre les étapes décrites dans la section précédente.

La question

Ces problèmes me font penser IComparablecomme inférieur IComparerou .OrderBy(), au point de ne voir aucun cas d'utilisation valide qui ne serait pas mieux servi par les alternatives.
Est-il toujours préférable d'utiliser IComparerou LINQ, ou y a-t-il des avantages / cas d'utilisation que je ne vois pas ici?

R. Schmitz
la source
2
Votre nouvelle exigence "trier deux façons différentes" est un hareng rouge. Pour le résoudre, il vous suffit de passer un comparateur différent à votre fonction de tri.
Robert Harvey
@RobertHarvey Alors vous n'utiliseriez IComparableplus, ce qui renforce mon argument.
R. Schmitz
N'oubliez pas que si vous utilisez les SortedXXXcollections, elles nécessitent soit que les éléments stockés le soient, IComparablesoit qu'ils soient IComparerfournis. Notez également qu'il est trivial d'inverser l'ordre de tri naturel avec un comparateur et de le faire fonctionner avec tous les IComparableobjets.
Berin Loritsch
2
Peu importe qu'il y ait deux interfaces différentes. IComparableest considéré comme le mécanisme de comparaison par défaut . IComparerest utilisé lorsque vous souhaitez remplacer le mécanisme de comparaison par défaut.
Robert Harvey
Exemple ReverseComparer<T>: gist.github.com/jackfarrington/078e7af7bc82482aa634
Berin Loritsch

Réponses:

14

IComparablea les restrictions que vous avez mentionnées, c'est exact. Il s'agit d'une interface qui était déjà disponible dans .NET Framework 1.0, où ces alternatives fonctionnelles et Linq n'étaient pas disponibles. Donc oui, on pourrait le voir comme un élément de framework obsolète qui est principalement conservé pour une compatibilité ascendante.

Cependant, pour de nombreuses structures de données simples, une méthode de tri est probablement suffisante ou naturelle. Pour ces cas, avoir un endroit canonique pour implémenter la relation d'ordre est toujours un bon moyen de garder le code SEC, au lieu de répéter toujours la même logique dans chaque appel à OrderBypartout.

Vous utilisez «heureusement IComparable dans votre application depuis 2 ans maintenant», comme vous l'avez écrit, il me semble donc que cela vous a bien servi pendant longtemps. Lorsque vous devez maintenant valider, modifier et tester tous les appels vers Sort, cela peut également être un signe que vous faisiez le même type de logique de tri à de nombreux endroits, ce qui n'est pas la faute IComparable. Cela pourrait donc être l'occasion de centraliser davantage cette logique en un seul endroit, rendant votre code plus SEC.

Doc Brown
la source
Bon point sur les structures de données simples. Cependant, le dernier paragraphe n'a pas 100% de sens pour moi. Si je ne l'avais pas utilisé IComparable, tout le code de tri préexistant aurait été laissé intact dans leurs vues respectives, alors que je ne ferais qu'ajouter un nouveau code de tri pour la nouvelle vue.
R. Schmitz
@ R.Schmitz Le tri préexistant aurait-il fonctionné correctement sans l' IComparableimplémentation que vous avez écrite?
Robert Harvey
3
@ R.Schmitz: Bien sûr, mais maintenant vous vous engagez à toujours fournir un comparateur (sauf si vous utilisez OrderBy, bien sûr). Avec IComparable, vous obtenez gratuitement une implémentation par défaut, et parfois vous n'avez même pas besoin d'écrire cette implémentation.
Robert Harvey
2
@ R.Schmitz: Votre dernier commentaire résume bien le point. J'irais un peu plus loin cependant. Supposons que vous ayez un type numérique comme BigInteger. S'il n'implémentait pas d' opérateurs / interfaces de comparaison, comment implémenteriez-vous même un IComparer vous - même ? Vous auriez besoin d'accéder aux structures de données internes pour le faire efficacement, ou pas du tout. Supposons que vous ayez un type comme Client; les propriétés publiques que vous souhaitez trier do ont comparateurs. Pour moi, c'est la différence: implémenter IComparable<T>s'il serait déraisonnable de s'attendre à ce que l'appelant implémente un comparateur.
Eric Lippert
1
If I hadn't used IComparable, all the pre-existing sorting code would have been left untouched in their respective views, while I'd only add new sorting code for the new view.Ce IComparablen'est pas parce que c'était une meilleure solution à l'époque que c'est la meilleure solution aujourd'hui . Votre premier commentaire ici implique que "c'est IComparable ou rien", ce qui n'est pas vrai, le problème aurait pu être résolu de différentes manières. Les applications peuvent croître en taille / échelle, et les choses qui semblaient convenables peuvent ne pas être en mesure de répondre aux demandes croissantes de l'application.
Flater
1

Je suis d'accord avec vos sentiments IComparable

Regardez les remarques sur Array.Sort()

  • Chaque élément du tableau doit implémenter l' IComparableinterface pour pouvoir effectuer des comparaisons avec tous les autres éléments du tableau. (ou une exception est levée)
  • Si le tri n'est pas terminé avec succès, les résultats ne sont pas définis.

Nous n'aurons probablement jamais la motivation maintenant, cependant! envisager object.Equals()une méthode sur chaque objet qui vous permet de comparer les objets les uns avec les autres pour voir s'ils sont "identiques"

Vous l'avez déjà là, mais vous avez été chargé d'ajouter que Array.Sort()vous voudrez peut-être ajouterobject.Compare(object)

Ewan
la source