Dlaczego interfejsy C # nie mogą zawierać pól?


223

Załóżmy na przykład, że chcę ICarinterfejs i że wszystkie implementacje będą zawierać pole Year. Czy to oznacza, że ​​każde wdrożenie musi być osobno zadeklarowane Year? Czy nie byłoby lepiej po prostu zdefiniować to w interfejsie?


21
Interfejsy nie mają implementacji, w tym celu należy użyć klasy abstrakcyjnej o właściwości Year
PostMan

6
Aby dodać do tego, co tu powiedziano, interfejsy są umowami, a pole jest szczegółem implementacji, w którym definiuje gniazdo w pamięci maszyny, w którym należy umieścić wartość (wskaźnik skalarny lub adresowy).
herzmeister

5
Ale jeśli pole jest publiczne, to jest częścią umowy, a nie tylko szczegółem wdrożenia, prawda?
Alex

Odpowiedzi:


238

Chociaż wiele innych odpowiedzi jest poprawnych na poziomie semantycznym, ciekawe jest również podejście do tego rodzaju pytań z poziomu szczegółów implementacji.

Interfejs można traktować jako zbiór gniazd , które zawierają metody . Gdy klasa implementuje interfejs, klasa musi poinformować środowisko wykonawcze, jak wypełnić wszystkie wymagane szczeliny. Kiedy powiesz

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

klasa mówi: „kiedy utworzysz instancję mnie, umieść odniesienie do Foo.M w gnieździe dla IFoo.M.

Następnie, gdy wykonujesz połączenie:

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

kompilator generuje kod, który mówi „zapytaj obiekt, która metoda znajduje się w gnieździe dla IFoo.M, i wywołaj tę metodę.

Jeśli interfejs jest zbiorem gniazd, które zawierają metody, niektóre z tych gniazd mogą również zawierać metody get i set właściwości, metody get and set indeksatora oraz metody dodawania i usuwania zdarzenia. Ale pole nie jest metodą . Nie ma „pola” związanego z polem, które można następnie „wypełnić” z odniesieniem do położenia pola. Dlatego interfejsy mogą definiować metody, właściwości, indeksatory i zdarzenia, ale nie pola.


26
Jedyną rzeczą, za którą czasami tęsknię, jest zdolność do definiowania stałych na poziomie interfejsu, która prawdopodobnie nie wymagałaby „gniazda” do obsługi w języku.
LBushkin

2
Lubię to wyjaśnienie w prostych słowach. Dzięki. „CLR przez C #” i „Essential .net wolumin 1” zawierają więcej szczegółów.
Sandeep GB

6
Dlaczego pole nie ma miejsca? i to samo pytanie z operatorami? Pamiętam, jak słyszałem o pisaniu kaczek za pomocą odbicia, aby sprawdzić, czy interfejs jest zaimplementowany, nawet jeśli klasa go nie odziedziczyła. Dlaczego nie można użyć odbicia (lub szczeliny) do podciągnięcia pola? wciąż piszę mój kod, więc może nie potrzebuję / chcę pól, ale zdziwiłem się, że nie mogę używać operatorów. operatory są dokładnie takie jak metody z mojego zrozumienia, z tym że nie wszystkie mogą być przeciążone ( An interface cannot contain constants, fields, operators. Z msdn.microsoft.com/en-us/library/ms173156.aspx )

@acidzombie: Pytanie, dlaczego interfejs nie może zdefiniować operatorów, jest inne (choć może powiązane) z tym, dlaczego nie może zawierać pól; Proponuję zadać kolejne pytanie, jeśli nadal jesteś tym zainteresowany.
Adam Robinson

1
@ b1nary.atr0phy dlaczego z polami? Np. Jeśli zadeklarowałem metodę int One(), implementacja public int One(){return 1;}nie jest polem.
Cześć Anioł

131

Interfejsy w języku C # mają na celu zdefiniowanie kontraktu, do którego klasa będzie się stosować - a nie konkretnej implementacji.

W tym duchu interfejsy C # umożliwiają zdefiniowanie właściwości - które wywołujący musi dostarczyć implementację dla:

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

Klasy implementujące mogą korzystać z auto-właściwości w celu uproszczenia implementacji, jeśli nie ma specjalnej logiki związanej z tą właściwością:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}

5
To nie wszystko, co jest publiczne, jest częścią umowy. Jeśli klasa ma publiczny int Rok, to czy nie oznacza to, że umowa klasowa ma pole typu Rok, które ma być na nim obecne i dostępne?
Didier A.

1
Późno na imprezę, ale nie, w tym przypadku oznacza to, że umowa ma Rok NIERUCHOMOŚCI, który każda trwająca klasa powinna wdrożyć. Właściwości są w rzeczywistości metodami get / set, które automatycznie generują pole zaplecza, jeśli nie jest wymagana żadna specjalna logika. Specjalna składnia służy tylko jaśniejszemu zapisowi.
user3613916

Jak mogę zdefiniować stałą wartość domyślną (np. 123) dla tej automatycznej implementacji Year?
lama12345

1
@ lama12345 Jestem również spóźniony na imprezę, ale od C # 6 (2015, .NET Framework 4.6 i .NET Core) możesz w tym celu użyć auto-właściwości. public int Year => 123;. Jednak w tym przypadku nie ma sensu mieć settera, więc interfejs musiałby zostać zdefiniowany za pomocąint Year { get; }
EriF89

56

Zadeklaruj jako właściwość:

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

47
Pytanie brzmi: „ Dlaczego interfejsy C # nie mogą zawierać pól?”. To nie dotyczy tego.
AakashM

14
Zgadzam się, że ta odpowiedź nie odpowiada na pytanie PO, ale dobrze rozwiązało mój problem.
Gorgen,

36

Eric Lippert go przybił, użyję innego sposobu, aby powiedzieć, co powiedział. Wszyscy członkowie interfejsu są wirtualni i wszyscy muszą zostać zastąpieni przez klasę, która dziedziczy interfejs. Nie podajesz wprost wirtualnego słowa kluczowego w deklaracji interfejsu ani nie używasz słowa kluczowego zastępującego w klasie, są one implikowane.

Wirtualne słowo kluczowe jest implementowane w .NET z metodami i tak zwaną tabelą v, tablicą wskaźników metod. Słowo kluczowe override wypełnia pole tabeli v innym wskaźnikiem metody, zastępując ten wygenerowany przez klasę podstawową. Właściwości, zdarzenia i indeksatory są wdrażane jako metody pod maską. Ale pola nie są. Interfejsy nie mogą zatem zawierać pól.


Podałeś techniczną / rzeczywistą nazwę (v-table) do koncepcji automatów wspomnianej przez Erica. Dziękuję za szczegóły Hans.
RBT

Jaki byłby sens tabeli V, gdyby interfejsy były niedozwolone w domyślnych implementacjach? To zmienia się w C # 8.0, ale to nie ma sensu.
AnthonyMonterrosa

19

Dlaczego nie mieć po prostu Yearnieruchomości, która jest całkowicie w porządku?

Interfejsy nie zawierają pól, ponieważ pola reprezentują określoną implementację reprezentacji danych, a ich ujawnienie spowodowałoby przerwanie enkapsulacji. Zatem posiadanie interfejsu z polem skutecznie koduje implementację zamiast interfejsu, co jest dziwnym paradoksem dla interfejsu!

Na przykład część Yearspecyfikacji może wymagać, aby ICarimplementatorzy zezwolili na przypisanie do Yearpóźniejszej wersji niż 1 lub przed 1900 rokiem. Nie można powiedzieć, że jeśli odsłoniłeś Yearpola - zdecydowanie lepiej jest użyć właściwości, aby wykonać pracę tutaj.


18

Krótka odpowiedź brzmi: tak, każdy typ implementacji będzie musiał stworzyć własną zmienną wspierającą. Wynika to z faktu, że interfejs jest analogiczny do umowy. Wszystko, co może zrobić, to określić konkretne publicznie dostępne fragmenty kodu, które typ implementacyjny musi udostępnić; nie może zawierać żadnego kodu.

Rozważ ten scenariusz, używając tego, co sugerujesz:

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

Mamy tutaj kilka problemów:

  • Ponieważ wszyscy członkowie interfejsu są - z definicji - publiczni, nasza zmienna wspierająca jest teraz dostępna dla każdego, kto korzysta z interfejsu
  • Która myBackingVariablebędzie MyClassużywać?

Najczęstszym podejściem jest zadeklarowanie interfejsu i klasy abonenckiej, która go implementuje. Zapewnia to elastyczność dziedziczenia po klasie abstrakcyjnej i uzyskania implementacji za darmo lub jawnego wdrożenia interfejsu i pozwolenia na dziedziczenie z innej klasy. Działa to mniej więcej tak:

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

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

7

Inni podali „Dlaczego”, więc dodam tylko, że twój interfejs może definiować Kontrolę; jeśli owiniesz go we właściwość:

public interface IView {
    Control Year { get; }
}


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

3

Interfejsy nie zawierają żadnej implementacji.

  1. Zdefiniuj interfejs z właściwością.
  2. Ponadto możesz zaimplementować ten interfejs w dowolnej klasie i korzystać z tej klasy w przyszłości.
  3. W razie potrzeby można zdefiniować tę właściwość jako wirtualną w klasie, aby można było zmodyfikować jej zachowanie.

3

Wiele już powiedziano, ale dla uproszczenia, oto moje zdanie. Interfejsy mają zawierać umowy metod do wdrożenia przez konsumentów lub klasy, a nie pola do przechowywania wartości.

Możesz argumentować, że w takim razie dlaczego właściwości są dozwolone? Tak więc prosta odpowiedź brzmi - właściwości są wewnętrznie zdefiniowane tylko jako metody.


1
Jeśli potrzebujesz dostępu do członka, po prostu uczyń go własnością, a będziesz dobry.
Unome

0

W tym celu możesz mieć klasę bazową Samochód, która implementuje pole roku, a wszystkie inne implementacje mogą dziedziczyć po nim.


0

Interfejs definiuje właściwości i metody instancji publicznych . Pola są zwykle prywatne lub w najwyższym stopniu chronione, wewnętrzne lub chronione wewnętrzne (termin „pole” zwykle nie jest używany w przypadku jakichkolwiek publicznych).

Jak stwierdzono w innych odpowiedziach, możesz zdefiniować klasę podstawową i zdefiniować właściwość chronioną, która będzie dostępna dla wszystkich spadkobierców.

Jedną z osobliwości jest to, że interfejs można w rzeczywistości zdefiniować jako wewnętrzny, ale ogranicza on użyteczność interfejsu i zwykle jest używany do definiowania wewnętrznej funkcjonalności, która nie jest używana przez inny kod zewnętrzny.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.