103 votes

Comment comparer des valeurs de types génériques?

Comment comparer les valeurs de types génériques ?

Je l'ai réduit à un exemple minimal :

public class Foo where T : IComparable
{
    private T _valeurMinimale = default(T);

    public bool EstDansLaPlage(T valeur) 
    {
        return (valeur >= _valeurMinimale); // <-- Erreur ici
    }
}

L'erreur est la suivante :

L'opérateur '>=' ne peut pas être appliqué aux opérandes du type 'T' et 'T'.

Que se passe-t-il !? T est déjà contraint à IComparable, et même en le contraignant à des types de valeurs (where T: struct), nous ne pouvons toujours pas appliquer les opérateurs <, >, <=, >=, == ou !=. (Je sais qu'il existe des solutions de contournement impliquant Equals() pour == et !=, mais cela ne fonctionne pas pour les opérateurs relationnels).

Alors, deux questions :

  1. Pourquoi observons-nous ce comportement étrange ? Qu'est-ce qui nous empêche de comparer les valeurs de types génériques qui sont connus pour être IComparable ? Cela ne va-t-il pas à l'encontre du but entier des contraintes génériques ?
  2. Comment résoudre cela, ou du moins contourner le problème ?

(Je réalise qu'il existe déjà plusieurs questions liées à ce problème en apparence simple - mais aucun des fils de discussion ne donne de réponse exhaustive ou viable, donc en voici une nouvelle.)

113voto

faester Points 6055

IComparable ne surcharge pas l'opérateur >=. Vous devriez utiliser

value.CompareTo(_minimumValue) >= 0

43voto

Palpie Points 884

Si value peut être nul, la réponse actuelle pourrait échouer. Utilisez plutôt quelque chose comme ceci :

Comparer.Default.Compare(value, _minimumValue) >= 0

35voto

Merlyn Morgan-Graham Points 31815

Problème avec la surcharge d'opérateurs

Malheureusement, les interfaces ne peuvent pas contenir d'opérateurs surchargés. Essayez de taper ceci dans votre compilateur :

public interface IInequalityComaparable
{
    bool operator >(T lhs, T rhs);
    bool operator >=(T lhs, T rhs);
    bool operator <(T lhs, T rhs);
    bool operator <=(T lhs, T rhs);
}

Je ne sais pas pourquoi ils n'ont pas permis cela, mais je suppose que cela compliquerait la définition du langage, et serait difficile à implémenter correctement pour les utilisateurs.

Ou bien, les concepteurs n'ont pas aimé le potentiel d'abus. Par exemple, imaginez faire une comparaison >= sur une class MagicMrMeow. Ou même sur une class Matrix. Que signifie le résultat concernant les deux valeurs ? Surtout quand il peut y avoir une ambiguïté ?

La solution officielle

Puisque l'interface ci-dessus n'est pas légale, nous avons l'interface IComparable pour contourner le problème. Elle n'implémente aucun opérateur et expose uniquement une méthode, int CompareTo(T other);

Voir http://msdn.microsoft.com/en-us/library/4d7sx9hd.aspx

Le résultat int est en fait un tri-bit, ou un tri-uaire (similaire à un Boolean, mais avec trois états). Ce tableau explique la signification des résultats :

Valeur              Signification

Moins que zéro     Cet objet est inférieur à l'objet spécifié par la méthode CompareTo.

Zéro               Cet objet est égal au paramètre de la méthode.

Plus que zéro      Cet objet est supérieur au paramètre de la méthode.

Utilisation de la solution de contournement

Pour faire l'équivalent de valeur >= _minimumValue, vous devez plutôt écrire :

valeur.CompareTo(_minimumValue) >= 0

7voto

David Yaw Points 13009
public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

Lorsque vous travaillez avec des génériques IComparable, tous les opérateurs inférieurs/supérieurs doivent être convertis en appels à CompareTo. Peu importe l'opérateur que vous utilisez, gardez les valeurs comparées dans le même ordre et comparez par rapport à zéro. (x y devient x.CompareTo(y) 0, où est >, >=, etc.)

De plus, je recommande que la contrainte générique que vous utilisez soit where T : IComparable. IComparable tout seul signifie que l'objet peut être comparé à n'importe quoi, comparer un objet aux autres du même type est probablement plus approprié.

4voto

TcKs Points 13249

Au lieu de valeur >= _minimValue utilisez la classe Comparer :

public bool IsInRange(T value ) {
    var result = Comparer.Default.Compare(value, _minimumValue);
    if ( result >= 0 ) { return true; }
    else { return false; }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X