Les interfaces : IEquatable et IComparable
Les interfaces sont essentiellement des contrats pour s'assurer qu'une classe implémente une ou plusieurs méthodes / accesseurs.
Le standard au niveau du nom des interfaces est un I majuscule suivi du nom de l'interface (ex: IEquatable, IComparable).
Déclaration de base
Dans l'interface, on déclare uniquement les signatures de méthodes, se terminant par un point-virgule.
Les 4 cas d'implémentation essentiels
Pour assurer une compatibilité totale avec les collections et algorithmes .NET (tris, recherches), une classe métier doit gérer les 4 cas suivants.
1. IEquatable (Version générique)
Permet de définir l'égalité basée sur les données réelles (règles métiers).
- Règle : Retourne false si l'autre objet est null.
2. Override de Equals(object)
Redéfinit la méthode héritée de Object.
- Approche : Utiliser as pour le transtypage, puis is null pour vérifier.
- Action : Relance la méthode typée (Cas 1).
3. IComparable (Version générique)
Définit l'ordre de tri des objets entre eux.
- Règle : Retourne 1 si l'autre objet est null (on est considéré plus grand que "rien").
- Retour : 0 si égal, positif si plus grand, négatif si plus petit.
4. IComparable (Version object)
Version non-générique pour la compatibilité avec les anciennes structures.
- Approche : Utiliser as pour le transtypage, puis is null pour vérifier.
- Action : Relance la méthode typée (Cas 3).
Exemple d'implémentation complète
Voici une classe Etudiant respectant rigoureusement ces 4 cas ainsi que les conventions de nommage C#.
/// <summary>
/// Représente un étudiant pouvant être comparé par sa moyenne.
/// </summary>
public class Etudiant : IEquatable<Etudiant>, IComparable<Etudiant>, IComparable
{
private string _nom;
private double _moyenne;
/// <summary>
/// Initialise une nouvelle instance de la classe <see cref="Etudiant"/>.
/// </summary>
public Etudiant(string nom, double moyenne)
{
_nom = nom;
_moyenne = moyenne;
}
// --- CAS 1 : IEquatable<Etudiant> (Générique) ---
/// <summary>
/// Vérifie l'égalité basée sur les données membres.
/// </summary>
public bool Equals(Etudiant other)
{
// Utilisation de "is null"
if (other is null)
{
return false;
}
return _nom == other._nom && _moyenne == other._moyenne;
}
// --- CAS 2 : Override de Equals(object) ---
/// <summary>
/// Relance la version typée après transtypage.
/// </summary>
public override bool Equals(object obj)
{
// Approche "as" demandée : c'est un "3 dans 1"
// 1. On transtype
// 2. On a null si ce n'est pas le bon type
// 3. Si obj est déjà null, temp reste null
Etudiant temp = obj as Etudiant;
// Utilisation de "is" pour le check de nullité
if (temp is null)
{
return false;
}
// Relance la méthode typée
return Equals(temp);
}
// --- CAS 3 : IComparable<Etudiant> (Générique) ---
/// <summary>
/// Compare la moyenne pour le tri.
/// </summary>
public int CompareTo(Etudiant other)
{
// Nous sommes plus grand que null
if (other is null)
{
return 1;
}
return _moyenne.CompareTo(other._moyenne);
}
// --- CAS 4 : IComparable (Object) ---
/// <summary>
/// Relance la version typée après transtypage.
/// </summary>
public int CompareTo(object obj)
{
// Approche "as" demandée : c'est un "3 dans 1"
// 1. On transtype
// 2. On a null si ce n'est pas le bon type
// 3. Si obj est déjà null, temp reste null
Etudiant temp = obj as Etudiant;
// Utilisation de "is" pour le check de nullité
if (temp is null)
{
return 1;
}
// Relance la méthode typée
return CompareTo(temp);
}
/// <summary>
/// Requis lors de la redéfinition de Equals.
/// </summary>
public override int GetHashCode() => HashCode.Combine(_nom, _moyenne);
}
Note sur StringComparison.Ordinal
Lorsqu'on compare des chaînes de caractères (ex: Nom), il est recommandé d'utiliser StringComparison.Ordinal pour une comparaison binaire stricte.
| Critère | StringComparison.Ordinal |
Autres (CurrentCulture, etc.) |
|---|---|---|
| Vitesse | ✅ Très rapide (binaire) | ❌ Plus lent |
| Prédictibilité | ✅ Identique partout | ❌ Varie selon la langue |
| Utilisation | Tris techniques, IDs, clés | Affichage utilisateur |