CodeGym /Cours /C# SELF /Méthodes par défaut dans les interfaces

Méthodes par défaut dans les interfaces

C# SELF
Niveau 24, Leçon 0
Disponible

1. Du "contrat pur" à une archi flexible

Avant C# 8, une interface c’était un contrat strict : tu veux l’implémenter — tu dois tout coder, pas le choix. Si on ajoutait un nouveau membre, toutes les implémentations existantes devaient le rajouter fissa, sinon le compilateur refusait de builder le projet.

Mais la vie, c’est plus compliqué. Imagine, tu maintiens une lib utilisée par des centaines de projets, et d’un coup tu dois ajouter une nouvelle méthode à une interface. Pas envie de casser la compatibilité ? C’est là que débarquent les méthodes par défaut (Default Interface Methods, DIM) !

C’est quoi le délire ?

Les méthodes par défaut te permettent de déclarer une implémentation direct dans l’interface. Le contrat devient plus souple : si une classe n’implémente pas la “nouveauté”, c’est l’implémentation par défaut qui sera utilisée. C’est comme une doublure au ciné : si l’acteur veut pas sauter du pont, c’est le cascadeur qui y va.

2. Syntaxe des Default Interface Methods

Comment déclarer une méthode avec implémentation dans une interface ?

C’est quasi comme une méthode normale, sauf que maintenant tu peux (et tu dois) écrire le corps de la méthode direct dans l’interface :


public interface ILogger
{
    void Log(string message);

    // Nouvelle méthode avec implémentation par défaut !
    void LogWarning(string message)
    {
        Log("[AVERTISSEMENT] " + message);
    }
}

Ici, LogWarning a déjà une implémentation ! Toute classe qui implémente ILogger doit juste coder Log, et LogWarning aura la version par défaut (sauf si elle en fait une à elle).

Compare : signature classique vs. moderne

Version Déclaration dans l’interface
Avant C# 8
void DoSomething();
C# 8 et plus
void DoSomething() { Console.WriteLine("Je bosse !"); }

Détails importants de la syntaxe

  • Pour une méthode avec implémentation, écris toujours le corps entre accolades.
  • Les méthodes par défaut ne peuvent pas être abstract.
  • Toutes les méthodes d’interface restent implicitement public.
  • Tu peux aussi déclarer des propriétés avec get/set par défaut (voir plus bas).

3. Exemples pratiques

Exemple 1. Assurer la compatibilité ascendante

Imaginons que ton appli a une interface pour sauvegarder des données :


public interface ISaveable
{
    void Save(string filePath);
}

Plus tard, tu veux ajouter la sauvegarde dans le cloud. Pas envie de toucher à cent classes ? Ajoute une méthode par défaut !


public interface ISaveable
{
    void Save(string filePath);

    // Nouvelle méthode avec implémentation "par défaut" !
    void SaveToCloud(string cloudService)
    {
        Console.WriteLine($"Je sauvegarde dans le cloud {cloudService} (par défaut — je fais rien)");
    }
}

Maintenant toutes les anciennes classes “savent” sauvegarder dans le cloud (même si pour l’instant ça fait juste un print).

Exemple 2. On étend l’interface du logger

Avant, on avait une interface de log toute simple :


public interface ILogger
{
    void Log(string message);
}

Ajoutons une méthode par défaut pour logger les erreurs :


public interface ILogger
{
    void Log(string message);

    void LogError(string message)
    {
        Log("[ERREUR] " + message);
    }
}

Une classe qui implémente ILogger n’est pas obligée d’implémenter LogError — la version par défaut marchera :


public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
    // On n’implémente pas LogError — version par défaut !
}

ILogger logger = new ConsoleLogger();
logger.Log("Tout va bien !");
logger.LogError("Oups, y’a un souci !"); // Appelle la version par défaut

Exemple 3. Méthodes par défaut + appli extensible

Ton appli gère différents types d’exports : fichier, BDD, réseau. L’interface :


public interface IExporter
{
    void Export(string data, string destination);

    // Nouvelle fonctionnalité — export en archive
    void ExportToArchive(string data, string archivePath)
    {
        Console.WriteLine("L’archivage n’est pas supporté par défaut.");
    }
}

Les plugins écrits par d’autres devs continueront de marcher, même s’ils connaissent pas la nouvelle méthode.

4. Comment marche l’appel des méthodes par défaut ?

Scénario "ancienne classe — nouvelle interface"

Si une classe n’implémente pas la méthode par défaut de l’interface, quand tu l’appelles via une référence d’interface, c’est l’implémentation de l’interface qui est utilisée. Si elle l’implémente — c’est la sienne qui tourne.


public class FileExporter : IExporter
{
    public void Export(string data, string destination)
    {
        Console.WriteLine("J’enregistre dans un fichier...");
    }
    // On n’implémente pas ExportToArchive — version par défaut
}

IExporter exporter = new FileExporter();
exporter.Export("données", "file.txt");        // Utilise FileExporter
exporter.ExportToArchive("données", "file.zip"); // Utilise la version par défaut !

Scénario "classe qui override la méthode par défaut"


public class AdvancedExporter : IExporter
{
    public void Export(string data, string destination)
    {
        Console.WriteLine("J’enregistre en mode avancé...");
    }

    public void ExportToArchive(string data, string archivePath)
    {
        Console.WriteLine("Archivage supporté !");
    }
}

IExporter exporter = new AdvancedExporter();
exporter.ExportToArchive("données", "file.zip"); // Là c’est la version de la classe !

5. Que peut-on faire d’autre avec les Default Interface Methods ?

Propriétés et events par défaut

Tu peux déclarer des propriétés avec une implémentation par défaut, si elles ont un corps get ou set :


public interface IHasId
{
    // Retourne 42 automatiquement, sauf si overridé
    int Id => 42;
}

public class Person : IHasId {}
Console.WriteLine(new Person().Id); // 42

Appeler des méthodes par défaut depuis le code de l’interface

Dans une interface, les méthodes par défaut et autres membres peuvent s’appeler entre eux :


public interface IDemo
{
    void Foo() => Bar();
    void Bar() => Console.WriteLine("BAR");
}

6. Limitations et particularités des Default Interface Methods

On peut déclarer des champs, des constructeurs ?

Non. Même avec les méthodes par défaut, une interface n’est toujours pas une classe. Pas de champs, pas de constructeurs, pas de destructeurs.

On peut utiliser base dans une interface ?

Oui, mais c’est spécial. Dans une méthode par défaut d’interface, tu peux appeler une méthode de l’interface parent en le précisant explicitement :


public interface IBase
{
    void Greet() => Console.WriteLine("Hello depuis IBase");
}

public interface IDerived : IBase
{
    void IBase.Greet()
    {
        Console.WriteLine("Salut depuis IDerived !");
        IBase.Greet(this); // Appel explicite de la méthode du parent
    }
}

Mais c’est rare pour les cas de base.

Que se passe-t-il en cas de conflit d’implémentations par défaut ?


public interface IA { void Foo() { Console.WriteLine("A"); } }
public interface IB { void Foo() { Console.WriteLine("B"); } }

// La classe n’implémente pas Foo explicitement :
public class C : IA, IB { }
// Erreur de compilation : impossible de choisir quelle implémentation prendre !

7. Erreurs classiques, limitations et particularités

Erreur marrante classique :
Certains essaient de déclarer des champs dans une interface après avoir vu les DIM — mais c’est toujours interdit. Aussi, si tu veux faire une méthode statique par défaut — avant C# 8 c’est pas possible. (Depuis la 8, tu peux avoir des méthodes statiques dans les interfaces, mais les implémentations par défaut statiques, c’est encore autre chose.)

Particularité : Diamond Problem ("problème du diamant")
Si ta classe implémente deux interfaces avec la même méthode par défaut, tu dois l’implémenter explicitement :


public class ConflictClass : IA, IB
{
    public void Foo() // à toi de choisir la version !
    {
        // Appel explicite de l’implémentation via l’interface (si besoin)
        ((IA)this).Foo();
        // ou
        ((IB)this).Foo();
    }
}

N’en abuse pas !
Les méthodes par défaut sauvent la compatibilité, mais si tu en abuses, ton archi va devenir “sale”, avec de la logique qui traîne dans les interfaces. Essaie de garder l’essentiel dans les classes, et utilise l’interface comme un vrai “contrat”.

2
Mission
C# SELF, niveau 24, leçon 0
Bloqué
Extension d'une interface avec une méthode par défaut
Extension d'une interface avec une méthode par défaut
2
Mission
C# SELF, niveau 24, leçon 0
Bloqué
Conflit d'interfaces et choix de l'implémentation
Conflit d'interfaces et choix de l'implémentation
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION