Czy w języku C # klasa może dziedziczyć z innej klasy i interfejsu?

134

Chcę wiedzieć, czy klasa może dziedziczyć po klasie i interfejsie. Poniższy przykładowy kod nie działa, ale myślę, że przekazuje to, co chcę zrobić. Powodem, dla którego chcę to zrobić, jest to, że w mojej firmie produkujemy urządzenia USB, szeregowe, Ethernet itp. Próbuję opracować ogólny komponent / interfejs, którego będę mógł używać do pisania programów dla wszystkich naszych urządzeń, które pomogą utrzymać te same typowe rzeczy (takie jak podłączanie, odłączanie, pobieranie oprogramowania układowego) dla wszystkich naszych aplikacji.

Aby dodać do tego pytania: Jeśli GenericDevice jest w innym projekcie, czy mogę umieścić interfejs IOurDevices w tym projekcie, a następnie sprawić, aby klasa USBDevice zaimplementowała interfejs, jeśli dodam odwołanie do pierwszego projektu? Ponieważ chciałbym odwołać się tylko do jednego projektu, a następnie zaimplementować różne interfejsy w zależności od tego, jakie jest urządzenie.

class GenericDevice
{
   private string _connectionState;
   public connectionState
   {
      get{return _connectionState; }
      set{ _connectionState = value;}
   }
}

interface IOurDevices
{
   void connectToDevice();
   void DisconnectDevice();
   void GetFirmwareVersion();
}

class USBDevice : IOurDevices : GenericDevice
{
   //here I would define the methods in the interface
   //like this...
   void connectToDevice()
   {
       connectionState = "connected";
   }
}

//so that in my main program I can do this...

class myProgram
{
   main()
   {
      USBDevice myUSB = new USBDevice();
      myUSB.ConnectToDevice;
   }
}
PICyourBrain
źródło
5
Na przyszłość w sekcji 10.1.4 specyfikacji języka C # opisano dokładnie, jak zadeklarować klasę, która ma wiele typów podstawowych.
Eric Lippert
@Eric Lippert: Czy mógłbyś mi pomóc zrozumieć ten przypadek, czy jest to właściwy sposób i czy będzie dostępny w przyszłości?
Gul Md Ershad

Odpowiedzi:

252

Tak. Próbować:

class USBDevice : GenericDevice, IOurDevice

Uwaga: Klasa bazowa powinna znajdować się przed listą nazw interfejsów.

Oczywiście nadal będziesz musiał zaimplementować wszystkie elementy członkowskie zdefiniowane przez interfejsy. Jeśli jednak klasa bazowa zawiera element członkowski, który pasuje do elementu członkowskiego interfejsu, element członkowski klasy bazowej może działać jako implementacja elementu członkowskiego interfejsu i nie jest wymagane ponowne jego ręczne wdrażanie.

Mehrdad Afshari
źródło
1
Tak, to działa! Dlaczego o tym nie pomyślałem! I do komentarzy poniżej. Dziękuję za wyjaśnienie mi tego (klasy nie dziedziczą interfejsów, interfejsy IMPLEMENT)
PICyourBrain
1
@Jordan, zwróć również uwagę, że klasa bazowa i lista dziedziczonych interfejsów są oddzielone przecinkami po początkowym dwukropku (przykład @ Mehrdad).
JMD
1
aby trochę rozszerzyć: jeśli twoja klasa bazowa implementuje interfejs, twoja klasa pochodna automatycznie implementuje ten interfejs - nawet bez USBDevice : IOurDevice. Dodanie implementacji jawnie nie wpływa na klasę bazową, ale może pomóc położyć nacisk na interfejs.
STW
1
@David, chociaż się nie mylisz, to nie terminologia uniemożliwiała działanie kodu @ Jordana. To była nieprawidłowa składnia.
JMD
2
+1 za wyjaśnienie pytania, które chciałem zadać, dotyczącego identycznych członków zarówno w bazie, jak iw interfejsie.
Riegardt Steyn
21

Nie, nie do końca. Ale może dziedziczyć z klasy i implementować jeden lub więcej interfejsów.

Przy omawianiu takich pojęć ważna jest jasna terminologia. Jedną z rzeczy, które zobaczysz, wyróżniającą na przykład pisma Jona Skeeta, zarówno tutaj, jak iw druku, jest to, że zawsze jest precyzyjny w sposobie, w jaki opisuje rzeczy.

David M.
źródło
9
Albo Eric Lippert, którego pismo jest bardzo dokładne.
Mathias,
21

Nie ma związku z pytaniem (odpowiedź Mehrdada powinna cię zachęcić) i mam nadzieję, że nie jest to trudne: klasy nie dziedziczą interfejsów, ale je implementują .

.NET nie obsługuje dziedziczenia wielokrotnego, więc proste sformułowanie warunków może pomóc w komunikacji. Klasa może dziedziczyć z jednej nadklasy i może implementować dowolną liczbę interfejsów.


W odpowiedzi na komentarz Erica ... przeprowadziłem dyskusję z innym deweloperem na temat tego, czy interfejsy „dziedziczą”, „implementują”, „wymagają” czy „przenoszą” interfejsy z deklaracją taką jak:

public interface ITwo : IOne

Odpowiedź techniczna jest taka, ITwoże dziedziczy IOnez kilku powodów:

  • Interfejsy nigdy nie mają implementacji, więc argumentowanie, że ITwo implementacje IOne jest całkowicie błędne
  • ITwodziedziczy IOnemetody, jeśli MethodOne()istnieje, IOneto jest również dostępny z ITwo. tj .: ((ITwo)someObject).MethodOne())jest poprawne, mimo że ITwonie zawiera wyraźnie definicjiMethodOne()
  • ... ponieważ tak mówi środowisko wykonawcze! typeof(IOne).IsAssignableFrom(typeof(ITwo))zwrotytrue

W końcu zgodziliśmy się, że interfejsy obsługują prawdziwe / pełne dziedziczenie. Brakujące funkcje dziedziczenia (takie jak przesłonięcia, abstrakcyjne / wirtualne metody dostępu itp.) Są niedostępne w interfejsach, a nie w dziedziczeniu interfejsu. Wciąż nie czyni to koncepcji prostą ani jasną, ale pomaga zrozumieć, co naprawdę dzieje się pod maską w świecie Erica :-)

STW
źródło
4
Chociaż, niestety, interfejsy dziedziczą inne interfejsy. Uważam, że ten dobór słów jest niefortunny, ale teraz z tym utknęliśmy. Wolę myśleć interfejsów jak wymagające innych interfejsów. To znaczy, kiedy mówisz „interface IFoo: IBar”, oznacza to, że „implementator IFoo musi również zaimplementować IBar”.
Eric Lippert
@Eric Zgadzam się, że to niejasne określenie i przedyskutowałem to z kolegą jakiś czas temu. W końcu zdecydowaliśmy, że powiedzenie „ITwo dziedziczy IOne”. Zaktualizuję moją odpowiedź z kilku małych powodów (tylko dlatego, że nie będą one wyraźnie pasować do komentarza).
STW
Pewnie; jeśli zdefiniujesz „A dziedziczy po B” jako oznaczenie „członkowie B są członkami A”, to interfejsy „dziedziczą” po interfejsach podstawowych. To rozsądna definicja. Ale ja wolę myśleć o „dziedziczeniu” jako o tym, że nie chodzi tylko o dzielenie się abstrakcyjnymi, niezaimplementowanymi elementami, ale raczej o dziedziczenie implementacji . Ponieważ interfejsy nie mają implementacji, myślenie o interfejsach jako dziedziczących po czymkolwiek jest nieco niepokojące. To subtelna i dyskusyjna kwestia.
Eric Lippert
Innym określeniem, które lubię, jest „przedłuż”. Możesz więc przeczytać „interfejs IFoo: IBar”, aby oznaczać, że IFoo rozszerza wymagania IBar.
jasonh
1
@Joan: masz rację, że klasy implementują (i nie mogą dziedziczyć) interfejsy. Eric podkreśla, że interfejsy mogą dziedziczyć inne interfejsy - co może być nieco trudne do zrozumienia, biorąc pod uwagę, że interfejsy są jedynie „specyfikacjami”, a nie implementacjami.
STW
1

Znalazłem odpowiedź na drugą część moich pytań. Tak, klasa może implementować interfejs należący do innej klasy, o ile interfejs jest zadeklarowany jako publiczny.

PICyourBrain
źródło
Nie we wszystkich przypadkach musi być publiczny. Po prostu dostępne. Na przykład class ContainsAll { private interface INested { /* ... */ } private class MyExample : INested { /* ... */ } }The MyExampleklasa implementuje zagnieżdżony prywatny interfejs. W innych przykładach zagnieżdżony interfejs (i klasa zawierająca) mógłby być internal. Wszystko zależy od tego, kto musi ich używać i zawracać sobie głowę nimi.
Jeppe Stig Nielsen