Warum können C # -Schnittstellen keine Felder enthalten?

223

Angenommen, ich möchte eine ICarSchnittstelle und alle Implementierungen enthalten das Feld Year. Bedeutet dies, dass jede Implementierung separat deklariert werden muss Year? Wäre es nicht schöner, dies einfach in der Benutzeroberfläche zu definieren?

deltanovember
quelle
21
Schnittstellen haben keine Implementierung, für diese verwenden Sie eine abstrakte Klasse mit der Eigenschaft Year
PostMan
6
Um das hier Gesagte zu ergänzen, sind Schnittstellen Verträge, und ein Feld ist ein Implementierungsdetail, indem es einen Steckplatz im Speicher des Computers definiert, in den ein Wert (entweder Skalar oder Adresszeiger) eingefügt werden soll.
Herzmeister
5
Aber wenn das Feld öffentlich ist, ist es Teil des Vertrags und nicht nur ein Implementierungsdetail, oder?
Alex

Antworten:

238

Obwohl viele der anderen Antworten auf semantischer Ebene richtig sind, finde ich es interessant, diese Art von Fragen auch auf der Ebene der Implementierungsdetails zu beantworten.

Eine Schnittstelle kann als eine Sammlung von Slots betrachtet werden , die Methoden enthalten . Wenn eine Klasse eine Schnittstelle implementiert, muss die Klasse der Laufzeit mitteilen, wie alle erforderlichen Slots ausgefüllt werden sollen. Wenn du sagst

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

Die Klasse sagt: "Wenn Sie eine Instanz von mir erstellen, geben Sie einen Verweis auf Foo.M in den Steckplatz für IFoo.M ein.

Wenn Sie dann einen Anruf tätigen:

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

Der Compiler generiert Code mit der Aufschrift "Fragen Sie das Objekt, welche Methode sich im Steckplatz für IFoo.M befindet, und rufen Sie diese Methode auf.

Wenn eine Schnittstelle eine Sammlung von Slots ist, die Methoden enthalten, können einige dieser Slots auch die Methoden get und set einer Eigenschaft, die Methoden get und set eines Indexers sowie die Methoden add und remove eines Ereignisses enthalten. Aber ein Feld ist keine Methode . Einem Feld ist kein "Slot" zugeordnet, den Sie dann mit einem Verweis auf die Feldposition "ausfüllen" können. Daher können Schnittstellen Methoden, Eigenschaften, Indexer und Ereignisse definieren, jedoch keine Felder.

Eric Lippert
quelle
26
Das einzige, was ich manchmal vermisse, ist eine Java-ähnliche Fähigkeit, Konstanten auf Schnittstellenebene zu definieren, für deren Unterstützung in der Sprache vermutlich kein "Slot" erforderlich wäre.
LBushkin
2
Ich mag die Erklärung in einfachen Worten. Vielen Dank. Weitere Informationen finden Sie unter "CLR über C #" und "Essential .net Volume 1".
Sandeep GB
6
Warum hat ein Feld keinen Steckplatz? und die gleiche Frage mit Betreibern? Ich erinnere mich, dass ich von Enten-Typisierung mit Reflektion gehört habe, um zu sehen, ob eine Schnittstelle implementiert ist, auch wenn die Klasse die Schnittstelle nicht geerbt hat. Warum kann keine Reflexion (oder ein Schlitz) verwendet werden, um ein Feld hochzuziehen? Ich schreibe immer noch meinen Code, daher brauche / möchte ich möglicherweise keine Felder, aber ich war überrascht, dass ich keine Operatoren verwenden kann. Operatoren sind genau wie Methoden nach meinem Verständnis, außer dass nicht alle überladen werden können ( An interface cannot contain constants, fields, operators. Von msdn.microsoft.com/en-us/library/ms173156.aspx )
@acidzombie: Die Frage, warum eine Schnittstelle keine Operatoren definieren kann, unterscheidet sich (obwohl möglicherweise verwandt) davon, warum sie keine Felder enthalten kann. Ich würde vorschlagen, eine weitere Frage zu stellen, wenn Sie immer noch daran interessiert sind.
Adam Robinson
1
@ b1nary.atr0phy warum mit Feldern? Wenn ich beispielsweise eine Methode deklariert habe int One(), ist die Implementierung public int One(){return 1;}kein Feld.
Hi-Angel
131

Schnittstellen in C # sollen den Vertrag definieren, an den sich eine Klasse halten wird - keine bestimmte Implementierung.

In diesem Sinne ermöglichen C # -Schnittstellen die Definition von Eigenschaften, für die der Aufrufer eine Implementierung bereitstellen muss:

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

Implementierende Klassen können Auto-Eigenschaften verwenden, um die Implementierung zu vereinfachen, wenn der Eigenschaft keine spezielle Logik zugeordnet ist:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}
LBushkin
quelle
5
Ist nicht alles, was öffentlich ist, Bestandteil des Vertrages? Wenn eine Klasse ein öffentliches int-Jahr hat, heißt das nicht, dass auf dem Klassenvertrag ein Feld vom Typ Jahr vorhanden und zugänglich ist?
Didier A.
1
Verspätet bei der Partei, aber nein, in diesem Fall bedeutet dies, dass der Vertrag ein IMMOBILIENJAHR hat, das jede bleibende Klasse umsetzen soll. Eigenschaften sind eigentlich get / set-Methoden, bei denen das Hintergrundfeld automatisch generiert wird, wenn keine spezielle Logik benötigt wird. Eine spezielle Syntax dient nur der klareren Notation.
user3613916
Wie kann ich einen konstanten Standardwert (wie 123) für diese automatische Implementierung von definieren Year?
lama12345
1
@ lama12345 Ich bin auch zu spät zur Party, aber seit C # 6 (2015, .NET Framework 4.6 und .NET Core) können Sie zu diesem Zweck eine Auto-Eigenschaft verwenden. public int Year => 123;. In diesem Fall macht es jedoch keinen Sinn, einen Setter zu haben, sodass die Schnittstelle mitint Year { get; }
EriF89
56

Deklarieren Sie es als eine Eigenschaft:

interface ICar {
   int Year { get; set; }
}
Tarydon
quelle
47
Die Frage lautet " Warum können C # -Schnittstellen keine Felder enthalten?". Das spricht das nicht an.
AakashM
14
Ich bin damit einverstanden, dass diese Antwort die OP-Frage nicht beantwortet, aber gut, sie hat mein Problem gelöst.
Gorgen
36

Eric Lippert hat es geschafft, ich werde anders sagen, was er gesagt hat. Alle Mitglieder einer Schnittstelle sind virtuell und müssen von einer Klasse überschrieben werden, die die Schnittstelle erbt. Sie schreiben weder das virtuelle Schlüsselwort explizit in die Schnittstellendeklaration, noch verwenden Sie das Schlüsselwort override in der Klasse, sie sind impliziert.

Das virtuelle Schlüsselwort wird in .NET mit Methoden und einer sogenannten V-Tabelle, einem Array von Methodenzeigern, implementiert. Das Schlüsselwort override füllt den v-Tabellen-Slot mit einem anderen Methodenzeiger und überschreibt den von der Basisklasse erzeugten. Eigenschaften, Ereignisse und Indexer werden als Methoden unter der Haube implementiert. Felder sind es aber nicht. Schnittstellen dürfen daher keine Felder enthalten.

Hans Passant
quelle
Sie haben den technischen / tatsächlichen Namen (V-Tabelle) für das von Eric erwähnte Konzept der Slots angegeben. Danke für das Detail Hans.
RBT
Was wäre der Sinn einer V-Tabelle, wenn Schnittstellen keine Standardimplementierungen zulassen? Dies ändert sich in C # 8.0, aber das ist nebensächlich.
AnthonyMonterrosa
19

Warum nicht einfach eine YearImmobilie haben, die vollkommen in Ordnung ist?

Schnittstellen enthalten keine Felder, da Felder eine bestimmte Implementierung der Datendarstellung darstellen und deren Offenlegung die Kapselung unterbrechen würde. Eine Schnittstelle mit einem Feld zu haben, würde also effektiv eine Implementierung anstelle einer Schnittstelle codieren, was ein merkwürdiges Paradoxon für eine Schnittstelle ist!

Zum Beispiel Yearkönnte ein Teil Ihrer Spezifikation erfordern, dass es für ICarImplementierer ungültig ist , die Zuordnung zu einem zuzulassen, Yeardas später als das aktuelle Jahr + 1 oder vor 1900 ist. Es gibt keine Möglichkeit zu sagen, dass, wenn Sie YearFelder verfügbar gemacht hätten - weitaus besser zu verwenden Eigenschaften stattdessen die Arbeit hier zu erledigen.

John Feminella
quelle
18

Die kurze Antwort lautet: Ja, jeder Implementierungstyp muss eine eigene Hintergrundvariable erstellen. Dies liegt daran, dass eine Schnittstelle einem Vertrag entspricht. Es kann lediglich bestimmte öffentlich zugängliche Codeteile angeben, die ein Implementierungstyp zur Verfügung stellen muss. Es kann keinen Code selbst enthalten.

Betrachten Sie dieses Szenario anhand Ihrer Vorschläge:

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 { }

Wir haben hier ein paar Probleme:

  • Da alle Mitglieder einer Schnittstelle per Definition öffentlich sind, ist unsere Hintergrundvariable jetzt jedem zugänglich, der die Schnittstelle verwendet
  • Welches myBackingVariablewird MyClassverwendet?

Der gebräuchlichste Ansatz besteht darin, die Schnittstelle und eine abstrakte Barebone-Klasse zu deklarieren, die sie implementiert. Dies gibt Ihnen die Flexibilität, entweder von der abstrakten Klasse zu erben und die Implementierung kostenlos zu erhalten oder die Schnittstelle explizit zu implementieren und von einer anderen Klasse erben zu dürfen. Es funktioniert ungefähr so:

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
quelle
7

Andere haben das "Warum" angegeben, daher füge ich nur hinzu, dass Ihre Schnittstelle ein Steuerelement definieren kann. Wenn Sie es in eine Eigenschaft einwickeln:

public interface IView {
    Control Year { get; }
}


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

Schnittstellen enthalten keine Implementierung.

  1. Definieren Sie eine Schnittstelle mit einer Eigenschaft.
  2. Außerdem können Sie diese Schnittstelle in jeder Klasse implementieren und diese Klasse in Zukunft verwenden.
  3. Bei Bedarf können Sie diese Eigenschaft in der Klasse als virtuell definieren, damit Sie ihr Verhalten ändern können.
Amit
quelle
3

Es wurde bereits viel gesagt, aber um es einfach zu machen, hier ist meine Meinung. Schnittstellen sollen Methodenverträge haben, die von den Verbrauchern oder Klassen implementiert werden sollen, und keine Felder zum Speichern von Werten.

Sie können argumentieren, warum dann Eigenschaften erlaubt sind? Die einfache Antwort lautet also: Eigenschaften werden intern nur als Methoden definiert.

Prakash Tripathi
quelle
1
Wenn Sie auf ein Mitglied zugreifen müssen, machen Sie es einfach zu einer Eigenschaft und Sie werden gut sein.
Unome
0

Zu diesem Zweck können Sie eine Car-Basisklasse haben, die das Jahr-Feld implementiert, und alle anderen Implementierungen können von diesem erben.


quelle
0

Eine Schnittstelle definiert Eigenschaften und Methoden öffentlicher Instanzen. Felder sind normalerweise privat oder höchstens geschützt, intern oder geschützt intern (der Begriff "Feld" wird normalerweise für nichts Öffentliches verwendet).

Wie in anderen Antworten angegeben, können Sie eine Basisklasse definieren und eine geschützte Eigenschaft definieren, auf die alle Erben zugreifen können.

Eine Kuriosität ist, dass eine Schnittstelle zwar als intern definiert werden kann, aber die Nützlichkeit der Schnittstelle einschränkt. Sie wird normalerweise verwendet, um interne Funktionen zu definieren, die von keinem anderen externen Code verwendet werden.

Frode N. Rosand
quelle