Pourquoi les interfaces C # ne peuvent-elles pas contenir de champs?

223

Par exemple, supposons que je veuille une ICarinterface 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?

deltanovember
la source
21
Les interfaces n'ont pas d'implémentation, pour cette utilisation une classe abstraite, avec la propriété Year
PostMan
6
Pour ajouter à ce qui a été dit ici, les interfaces sont des contrats et un champ est un détail d'implémentation en ce sens qu'il définit un emplacement dans la mémoire de la machine pour y mettre une valeur (scalaire ou pointeur d'adresse).
herzmeister
5
Mais si le domaine est public, cela fait partie du contrat et pas seulement un détail de mise en œuvre, non?
Alex

Réponses:

238

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

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

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:

IFoo ifoo = new Foo();
ifoo.M();

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.

Eric Lippert
la source
26
La seule chose que je manque parfois est une capacité de type java pour définir des constantes au niveau de l'interface, ce qui ne nécessiterait probablement pas de "slot" pour la prise en charge dans le langage.
LBushkin
2
J'aime l'explication en termes simples. Merci. "CLR via C #" et "Essential .net volume 1" fournissent plus de détails.
Sandeep GB
6
Pourquoi un champ n'a-t-il pas de slot? et même question avec les opérateurs? Je me souviens avoir entendu parler du typage de canard en utilisant la réflexion pour voir si une interface est implémentée même si la classe n'a pas hérité de l'interface. Pourquoi la réflexion (ou une fente) ne peut-elle pas être utilisée pour tirer un champ? J'écris toujours mon code, donc je n'ai peut-être pas besoin / je veux des champs mais j'ai été surpris de constater que je ne peux pas utiliser d'opérateurs. les opérateurs sont exactement comme les méthodes de ma compréhension, sauf que tous ne peuvent pas être surchargés ( An interface cannot contain constants, fields, operators. De msdn.microsoft.com/en-us/library/ms173156.aspx )
@acidzombie: La question de savoir pourquoi une interface ne peut pas définir d'opérateurs est différente (bien que peut-être liée) à pourquoi elle ne peut pas contenir de champs; Je suggère de poster une autre question si cela vous intéresse toujours.
Adam Robinson
1
@ b1nary.atr0phy pourquoi avec des champs? Par exemple, si j'ai déclaré une méthode int One(), l'implémentation public int One(){return 1;}n'est pas un champ.
Hi-Angel
131

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:

interface ICar
{
    int Year { get; set; }
}

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é:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}
LBushkin
la source
5
Tout ce qui est public ne fait-il pas partie du contrat. Si une classe a public int Year, cela ne dit-il pas que le contrat de classe a un champ de type Year pour y être présent et accessible?
Didier A.
1
En retard à la fête, mais non, dans ce cas, cela signifie que le contrat a une année de propriété que toute classe respectueuse est censée mettre en œuvre. Les propriétés sont en fait des méthodes get / set, qui ont un champ de sauvegarde généré automatiquement si aucune logique spéciale n'est nécessaire. La syntaxe spéciale est juste pour une notation plus claire.
user3613916
Comment puis-je définir une valeur par défaut constante (comme 123) pour cette implémentation automatique de Year?
lama12345
1
@ lama12345 Je suis également en retard à la fête, mais depuis C # 6 (2015, .NET Framework 4.6 et .NET Core), vous pouvez utiliser une propriété automatique à cette fin. 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; }
EriF89
56

Déclarez-le comme une propriété:

interface ICar {
   int Year { get; set; }
}
Tarydon
la source
47
La question est " Pourquoi les interfaces C # ne peuvent-elles pas contenir de champs?". Cela ne règle pas cela.
AakashM
14
Je suis d'accord que cette réponse ne répond pas à la question OP, mais bon, cela a résolu mon problème.
Gorgen
36

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.

Hans Passant
la source
Vous avez fourni le nom technique / réel (v-table) au concept de slots mentionné par Eric. Merci pour le détail Hans.
RBT
À quoi servirait une v-table si les interfaces sont des implémentations par défaut interdites? Cela change en C # 8.0, mais c'est à côté du point.
AnthonyMonterrosa
19

Pourquoi ne pas simplement avoir une Yearproprié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 Yearspécification peut exiger qu'il ne soit pas valide pour les ICarimplémenteurs d'autoriser l'affectation à un Yearqui est postérieur à l'année en cours + 1 ou avant 1900. Il n'y a aucun moyen de dire que si vous aviez des Yearchamps exposés - il vaut mieux utiliser propriétés à la place pour faire le travail ici.

John Feminella
la source
18

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:

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:

  • Parce que tous les membres d'une interface sont - par définition - publics, notre variable de support est désormais exposée à toute personne utilisant l'interface
  • Ce qui myBackingVariableva MyClassutiliser?

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:

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}
Adam Robinson
la source
7

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é:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}
zomf
la source
3

Les interfaces ne contiennent aucune implémentation.

  1. Définissez une interface avec une propriété.
  2. De plus, vous pouvez implémenter cette interface dans n'importe quelle classe et utiliser cette classe à l'avenir.
  3. Si nécessaire, vous pouvez avoir cette propriété définie comme virtuelle dans la classe afin de pouvoir modifier son comportement.
Amit
la source
3

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.

Prakash Tripathi
la source
1
Si vous avez besoin d'accéder à un membre, faites-en une propriété et tout ira bien.
Unome
0

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
0

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.

Frode N. Rosand
la source