Par exemple, supposons que je veuille une ICar
interface et que toutes les implémentations contiendront le champ Year
. Est-ce à dire que chaque implémentation doit déclarer séparément Year
? Ne serait-il pas plus agréable de simplement définir cela dans l'interface?
223
Réponses:
Bien que la plupart des autres réponses soient correctes au niveau sémantique, je trouve intéressant d'aborder également ce type de questions au niveau des détails de l'implémentation.
Une interface peut être considérée comme une collection d' emplacements contenant des méthodes . Lorsqu'une classe implémente une interface, la classe doit indiquer au runtime comment remplir tous les emplacements requis. Quand tu dis
la classe dit "lorsque vous créez une instance de moi, remplissez une référence à Foo.M dans l'emplacement pour IFoo.M.
Ensuite, lorsque vous passez un appel:
le compilateur génère du code qui dit "demandez à l'objet quelle méthode se trouve dans l'emplacement pour IFoo.M, et appelez cette méthode.
Si une interface est une collection d'emplacements qui contiennent des méthodes, certains de ces emplacements peuvent également contenir les méthodes get et set d'une propriété, les méthodes get et set d'un indexeur et les méthodes add et remove d'un événement. Mais un champ n'est pas une méthode . Il n'y a pas de "fente" associée à un champ que vous pouvez ensuite "remplir" avec une référence à l'emplacement du champ. Et par conséquent, les interfaces peuvent définir des méthodes, des propriétés, des indexeurs et des événements, mais pas des champs.
la source
An interface cannot contain constants, fields, operators
. De msdn.microsoft.com/en-us/library/ms173156.aspx )int One()
, l'implémentationpublic int One(){return 1;}
n'est pas un champ.Les interfaces en C # sont destinées à définir le contrat auquel une classe adhérera - pas une implémentation particulière.
Dans cet esprit, les interfaces C # permettent de définir des propriétés - pour lesquelles l'appelant doit fournir une implémentation pour:
Les classes d'implémentation peuvent utiliser les propriétés automatiques pour simplifier l'implémentation, s'il n'y a pas de logique spéciale associée à la propriété:
la source
Year
?public int Year => 123;
. Cependant, dans ce cas, cela n'a aucun sens d'avoir un setter, donc l'interface devrait être définie avecint Year { get; }
Déclarez-le comme une propriété:
la source
Eric Lippert l'a cloué, je vais utiliser une manière différente de dire ce qu'il a dit. Tous les membres d'une interface sont virtuels et doivent tous être remplacés par une classe qui hérite de l'interface. Vous n'écrivez pas explicitement le mot clé virtuel dans la déclaration d'interface, ni n'utilisez le mot clé override dans la classe, ils sont implicites.
Le mot-clé virtuel est implémenté dans .NET avec des méthodes et une soi-disant v-table, un tableau de pointeurs de méthode. Le mot-clé override remplit le slot v-table avec un pointeur de méthode différent, écrasant celui produit par la classe de base. Les propriétés, les événements et les indexeurs sont implémentés comme des méthodes sous le capot. Mais les champs ne le sont pas. Les interfaces ne peuvent donc pas contenir de champs.
la source
Pourquoi ne pas simplement avoir une
Year
propriété, ce qui est parfaitement bien?Les interfaces ne contiennent pas de champs car les champs représentent une implémentation spécifique de la représentation des données, et les exposer romprait l'encapsulation. Ainsi, avoir une interface avec un champ serait effectivement coder vers une implémentation au lieu d'une interface, ce qui est un curieux paradoxe pour une interface!
Par exemple, une partie de votre
Year
spécification peut exiger qu'il ne soit pas valide pour lesICar
implémenteurs d'autoriser l'affectation à unYear
qui est postérieur à l'année en cours + 1 ou avant 1900. Il n'y a aucun moyen de dire que si vous aviez desYear
champs exposés - il vaut mieux utiliser propriétés à la place pour faire le travail ici.la source
La réponse courte est oui, chaque type d'implémentation devra créer sa propre variable de support. En effet, une interface est analogue à un contrat. Tout ce qu'il peut faire est de spécifier des morceaux de code particuliers accessibles au public qu'un type d'implémentation doit rendre disponibles; il ne peut contenir aucun code lui-même.
Considérez ce scénario en utilisant ce que vous proposez:
Nous avons quelques problèmes ici:
myBackingVariable
vaMyClass
utiliser?L'approche la plus courante est de déclarer l'interface et une classe abstraite barebones qui l'implémente. Cela vous donne la possibilité d'hériter de la classe abstraite et d'obtenir l'implémentation gratuitement, ou d'implémenter explicitement l'interface et d'être autorisé à hériter d'une autre classe. Cela fonctionne comme ceci:
la source
D'autres ont donné le «pourquoi», je vais donc simplement ajouter que votre interface peut définir un contrôle; si vous l'enveloppez dans une propriété:
la source
Les interfaces ne contiennent aucune implémentation.
la source
Beaucoup a déjà été dit, mais pour faire simple, voici mon point de vue. Les interfaces sont destinées à avoir des contrats de méthode à mettre en œuvre par les consommateurs ou les classes et à ne pas avoir de champs pour stocker les valeurs.
Vous pouvez faire valoir que, alors, pourquoi les propriétés sont autorisées? La réponse est donc simple: les propriétés sont définies en interne uniquement comme des méthodes.
la source
Pour cela, vous pouvez avoir une classe de base Car qui implémente le champ année, et toutes les autres implémentations peuvent en hériter.
la source
Une interface définit les propriétés et méthodes des instances publiques . Les champs sont généralement privés, ou tout au plus protégés, internes ou internes protégés (le terme "champ" n'est généralement pas utilisé pour quelque chose de public).
Comme indiqué par d'autres réponses, vous pouvez définir une classe de base et définir une propriété protégée qui sera accessible à tous les héritiers.
Une particularité est qu'une interface peut en fait être définie comme interne, mais elle limite l'utilité de l'interface, et elle est généralement utilisée pour définir une fonctionnalité interne qui n'est pas utilisée par un autre code externe.
la source