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.)

2voto

supercat Points 25534

Comme d'autres l'ont déclaré, il est nécessaire d'utiliser explicitement la méthode CompareTo. La raison pour laquelle on ne peut pas utiliser les interfaces avec les opérateurs est qu'il est possible pour une classe d'implémenter un nombre arbitraire d'interfaces, sans classement clair entre elles. Supposons que l'on tente de calculer l'expression "a = foo + 5;" lorsque foo implémente six interfaces, toutes définissant un opérateur "+" avec un second argument entier; quelle interface devrait être utilisée pour l'opérateur?

Le fait que les classes puissent dériver de plusieurs interfaces rend les interfaces très puissantes. Malheureusement, cela oblige souvent à être plus explicite sur ce que l'on veut vraiment faire.

1voto

parapura rajkumar Points 16597

IComparable force uniquement une fonction appelée CompareTo(). Vous ne pouvez donc pas appliquer les opérateurs que vous avez mentionnés

-1voto

InteXX Points 161

J'ai pu utiliser la réponse de Peter Hedberg pour créer quelques méthodes d'extension surchargées pour les génériques. Notez que la méthode CompareTo ne fonctionne pas ici, car le type T est inconnu et n'implémente pas cette interface. Cela dit, je suis intéressé à voir d'autres alternatives.

[DebuggerStepThrough]
public void RemoveDuplicates(this List Instance)
{
  Instance.RemoveDuplicates((X, Y) => Comparer.Default.Compare(X, Y));
}

[DebuggerStepThrough]
public void RemoveDuplicates(this List Instance, Comparison Comparison)
{
  Instance.RemoveDuplicates(new List>() { Comparison });
}

[DebuggerStepThrough]
public void RemoveDuplicates(this List Instance, List> Comparisons)
{
  List oResults = new List();

  for (int i = 0; i <= Instance.Count - 1; i++)
  {
    for (int j = Instance.Count - 1; j >= i + 1; j += -1)
    {
      oResults.Clear();

      foreach (Comparison oComparison in Comparisons)
        oResults.Add(oComparison(Instance[i], Instance[j]) == 0);

      if (oResults.Any(R => R))
        Instance.RemoveAt(j);
    }
  }
}

--EDIT--

J'ai pu simplifier cela en contraignant T à IComparable(Of T) sur toutes les méthodes, comme l'a indiqué l'OP. Notez que cette contrainte nécessite que le type T implémente également IComparable(Of ).

[DebuggerStepThrough]
public void RemoveDuplicates(this List Instance) where T : IComparable
{
    Instance.RemoveDuplicates((X, Y) => X.CompareTo(Y));
}

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