Par exemple, supposons que je veuille une interface ICar
et que toutes les implémentations contiennent le champ Year
. Cela signifie-t-il que chaque implémentation doit déclarer séparément Year
? Ne serait-il pas plus agréable de simplement définir ceci dans l'interface?
Bien que de nombreuses autres réponses soient correctes au niveau sémantique, je trouve intéressant d’aborder également ce type de questions à partir du niveau de détail de la mise en œuvre.
Une interface peut être considérée comme un ensemble de slots contenant des méthodes Lorsqu'une classe implémente une interface, elle doit indiquer au moteur d'exécution comment remplir tous les emplacements requis. Quand tu dis
interface IFoo { void M(); }
class Foo : IFoo { public void M() { ... } }
la classe dit "lorsque vous créez une instance de moi, insérez une référence à Foo.M dans l'emplacement pour IFoo.M.
Puis quand vous appelez:
IFoo ifoo = new Foo();
ifoo.M();
le compilateur génère un code qui dit "demande à l'objet quelle méthode est dans l'emplacement pour IFoo.M et appelle cette méthode.
Si une interface est un ensemble d'emplacements contenant des méthodes, ces emplacements peuvent également contenir les méthodes get et set d'une propriété, les méthodes get et set d'un indexeur, ainsi que les méthodes d'ajout et de suppression d'un événement. Mais n champ n'est pas une méthode. Il n'y a pas d '"emplacement" associé à un champ que vous pouvez ensuite "compléter" 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.
Les interfaces en C # sont destinées à définir le contrat qu'une classe va adhérer - 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:
interface ICar
{
int Year { get; set; }
}
Les classes d'implémentation peuvent utiliser des propriétés automatiques pour simplifier l'implémentation, s'il n'y a pas de logique particulière associée à la propriété:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Déclarez-le comme une propriété:
interface ICar {
int Year { get; set; }
}
Eric Lippert a bien compris, je vais utiliser une autre façon de dire ce qu'il a dit. Tous les membres d'une interface sont virtuels et ils doivent tous être remplacés par une classe qui hérite de l'interface. Vous n'écrivez pas explicitement le mot clé virtual dans la déclaration d'interface, ni utilisez le mot clé override dans la classe, ils sont implicites.
Le mot clé virtual est implémenté dans .NET avec des méthodes et une soi-disant v-table, un tableau de pointeurs de méthodes. Le mot-clé override remplit l'emplacement de la table virtuelle avec un pointeur de méthode différent, en remplaçant celui produit par la classe de base. Les propriétés, les événements et les indexeurs sont implémentés sous forme de méthodes. Mais les champs ne sont pas. Les interfaces ne peuvent donc pas contenir de champs.
Pourquoi ne pas simplement avoir une propriété Year
qui convient parfaitement?
Les interfaces ne contiennent pas de champs car ceux-ci représentent une implémentation spécifique de la représentation des données et leur exposition risquerait de rompre l'encapsulation. Ainsi, avoir une interface avec un champ serait effectivement coder pour une implémentation au lieu d’une interface, ce qui est un curieux paradoxe pour une interface!
Par exemple, une partie de votre spécification Year
peut nécessiter qu'elle ne soit pas valide pour que les développeurs ICar
autorisent l'affectation à un Year
qui est postérieur à l'année en cours + 1 ou avant 1900. Il n'y a pas façon de dire que si vous aviez exposé Year
champs - il est préférable d’utiliser des propriétés plutôt que de faire le travail ici.
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 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 suggérez:
public interface InterfaceOne
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public interface InterfaceTwo
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public class MyClass : InterfaceOne, InterfaceTwo { }
Nous avons quelques problèmes ici:
myBackingVariable
utilisera MyClass
?L'approche la plus courante consiste à déclarer l'interface et une classe abstraite barebone 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'autoriser l'héritage d'une autre classe. Cela fonctionne à peu près comme ceci:
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
D'autres ont donné le "Pourquoi", je vais donc ajouter que votre interface peut définir un contrôle; si vous l'enveloppez dans une propriété:
public interface IView {
Control Year { get; }
}
public Form : IView {
public Control Year { get { return uxYear; } } //numeric text box or whatever
}
Les interfaces ne contiennent aucune implémentation.
Beaucoup a déjà été dit, mais pour simplifier, voici ce que je pense. Les interfaces sont destinées à avoir des contrats de méthode à implémenter par les consommateurs ou les classes et non à avoir des champs pour stocker des valeurs.
Vous pouvez soutenir que, alors, pourquoi les propriétés sont autorisées? La réponse est simple: les propriétés sont définies en interne en tant que méthodes uniquement.
Une interface définit les propriétés et méthodes de l'instance public. Les champs sont généralement privés, ou du moins protégés, internes ou internes internes (le terme "champ" n’est généralement utilisé pour rien du public).
Comme indiqué par d'autres réponses, vous pouvez définir une classe de base et 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 en limite l’utilité 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.
Pour cela, vous pouvez avoir une classe de base Car qui implémente le champ year et toutes les autres implémentations peuvent en hériter.