Interfaces : définir le comportement pour plusieurs types

Une interface contient des définitions pour un groupe de fonctionnalités connexes qu'une class non abstraite ou un struct doivent implémenter. Une interface peut définir des méthodes static, qui doivent avoir une implémentation. Une interface peut définir une implémentation par défaut pour les membres. Une interface peut ne pas déclarer de données d’instance telles que des champs, des propriétés implémentées automatiquement ou des événements similaires à une propriété.

En utilisant des interfaces, vous pouvez, par exemple, inclure le comportement de plusieurs sources dans une classe. Cette fonctionnalité est importante en C#, car le langage ne prend pas en charge l'héritage multiple de classes. De plus, vous devez utiliser une interface si vous voulez simuler l'héritage pour des structs, car ils ne peuvent en fait pas hériter d'un autre struct ou d'une autre classe.

Vous définissez une interface à l'aide du mot clé interface, comme le montre l'exemple suivant.

interface IEquatable<T>
{
    bool Equals(T obj);
}

Le nom de l’interface doit être un nom d’identificateur C# valide. Par convention, les noms d’interface commencent par un I majuscule.

Toute classe ou tout struct qui implémentent l'interface IEquatable<T> doivent contenir une définition pour une méthode Equals qui correspond à la signature spécifiée par l'interface. Ainsi, vous pouvez compter sur une classe qui implémente IEquatable<T> pour contenir une méthode Equals avec laquelle une instance de la classe peut déterminer si elle est égale à une autre instance de la même classe.

La définition de IEquatable<T> ne fournit pas d'implémentation pour Equals. Une classe ou un struct peut implémenter plusieurs interfaces, mais une classe peut uniquement hériter d’une classe unique.

Pour plus d'informations sur les classes abstraites, consultez Classes abstract et sealed et membres de classe.

Les interfaces peuvent contenir des méthodes d’instance, propriétés, événements, indexeurs ou toute combinaison de ces quatre types de membres. Les interfaces peuvent contenir des constructeurs statiques, des champs, des constantes ou des opérateurs. À compter de C# 11, les membres d’interface qui ne sont pas des champs peuvent être static abstract. Une interface ne peut pas contenir de champs d’instance, de constructeurs d’instance ou de finaliseurs. Les membres de l’interface sont publics par défaut et vous pouvez spécifier explicitement des modificateurs d’accessibilité, tels que public, protected, internal, private, protected internal ou private protected. Un membre private doit avoir une implémentation par défaut.

Pour implémenter un membre d'interface, le membre correspondant de la classe d'implémentation doit être public, non statique et porter le même nom et la même signature que le membre d'interface.

Notes

Lorsqu’une interface déclare des membres statiques, un type implémentant cette interface peut également déclarer des membres statiques avec la même signature. Celles-ci sont distinctes et identifiées de manière unique par le type déclarant le membre. Le membre statique déclaré dans un type ne remplace pas le membre statique déclaré dans l’interface.

Une classe ou un struct qui implémente une interface doit fournir une implémentation pour tous les membres déclarés sans implémentation par défaut fournie par l’interface. Toutefois, si une classe de base implémente une interface, toute classe dérivée de la classe de base hérite de cette implémentation.

L'exemple suivant illustre une implémentation de l'interface IEquatable<T>. La classe d'implémentation, Car, doit fournir une implémentation de la méthode Equals.

public class Car : IEquatable<Car>
{
    public string? Make { get; set; }
    public string? Model { get; set; }
    public string? Year { get; set; }

    // Implementation of IEquatable<T> interface
    public bool Equals(Car? car)
    {
        return (this.Make, this.Model, this.Year) ==
            (car?.Make, car?.Model, car?.Year);
    }
}

Les propriétés et indexeurs d’une classe peuvent définir des accesseurs supplémentaires pour une propriété ou un indexeur qui est défini dans une interface. Par exemple, une interface peut déclarer une propriété qui a un accesseur get. La classe qui implémente l’interface peut déclarer la même propriété avec à la fois un accesseur get et un accesseur set. Toutefois, si la propriété ou l’indexeur utilisent une implémentation explicite, les accesseurs doivent correspondre. Pour plus d'informations sur l'implémentation explicite, consultez les pages Implémentation d'interface explicite et Propriétés d'interface.

Des interfaces peuvent hériter d'une ou de plusieurs interfaces. L’interface dérivée hérite des membres de ses interfaces de base. Une classe qui implémente une interface dérivée doit implémenter tous les membres de l’interface dérivée, y compris tous les membres des interfaces de base de l’interface dérivée. Cette classe peut être convertie implicitement en l’interface dérivée ou l’une de ses interfaces de base. Une classe peut inclure une interface plusieurs fois par le biais de classes de base qu’elle hérite ou d’interfaces dont d’autres interfaces héritent. Toutefois, la classe peut fournir une implémentation d'une interface une seule fois et uniquement si la classe déclare l'interface dans le cadre de la définition de la classe (class ClassName : InterfaceName). Si l'interface est héritée car vous avez hérité d'une classe de base qui implémente l'interface, la classe de base fournit l'implémentation des membres de l'interface. Toutefois, la classe dérivée peut réimplémenter des membres d’interface virtuels au lieu d’utiliser l’implémentation héritée. Lorsque des interfaces déclarent une implémentation par défaut d’une méthode, toute classe implémentant cette interface hérite de cette implémentation (vous devez caster l’instance de classe vers le type d’interface pour accéder à l’implémentation par défaut sur le membre Interface).

Une classe de base peut également implémenter des membres d'interface à l'aide de membres virtuels. Dans ce cas, une classe dérivée peut modifier le comportement de l'interface en substituant les membres virtuels. Pour plus d'informations sur les membres virtuels, consultez la page Polymorphisme.

Résumé des interfaces

Une interface possède les propriétés suivantes :

  • Dans les versions C# antérieures à 8.0, une interface est comme une classe de base abstraite avec uniquement des membres abstraits. Une classe ou un struct qui implémente l'interface doivent implémenter tous ses membres.
  • À compter de C# 8.0, une interface peut définir des implémentations par défaut pour tout ou partie de ses membres. Une classe ou un struct qui implémente l’interface n’a pas besoin d’implémenter les membres qui ont des implémentations par défaut. Pour plus d’informations, consultez Méthodes d’interface par défaut.
  • Une interface ne peut pas être instanciée directement. Ses membres sont implémentées par une classe ou un struct qui implémentent l'interface.
  • Une classe ou un struct peuvent implémenter plusieurs interfaces. Une classe peut hériter d'une classe de base et également implémenter une ou plusieurs interfaces.