Trong C #, một lớp có thể kế thừa từ một lớp khác và một giao diện không?


130

Tôi muốn biết nếu một lớp có thể kế thừa từ một lớp và một giao diện. Mã ví dụ dưới đây không hoạt động nhưng tôi nghĩ nó truyền tải những gì tôi muốn làm. Lý do mà tôi muốn làm điều này là vì tại công ty của tôi, chúng tôi sản xuất thiết bị USB, nối tiếp, Ethernet, v.v. Tôi đang cố gắng phát triển một thành phần / giao diện chung mà tôi có thể sử dụng để viết chương trình cho tất cả các thiết bị của chúng tôi sẽ giúp giữ những thứ phổ biến (như kết nối, ngắt kết nối, nhận phần sụn) giống nhau cho tất cả các ứng dụng của chúng tôi.

Để thêm vào câu hỏi này: Nếu GenericDevice nằm trong dự án khác, tôi có thể đặt giao diện IOurDevices trong dự án đó không, sau đó làm cho lớp USBDevice thực hiện giao diện nếu tôi thêm một tham chiếu đến dự án đầu tiên? Bởi vì chỉ muốn tham khảo một dự án và sau đó thực hiện các giao diện khác nhau tùy thuộc vào thiết bị là gì.

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

5
Để tham khảo trong tương lai của bạn, phần 10.1.4 của đặc tả C # mô tả chính xác cách khai báo một lớp có nhiều loại cơ sở.
Eric Lippert

@Eric Lippert: Bạn có thể vui lòng giúp tôi hiểu về trường hợp này cho dù đó là cách chính xác và sẽ có sẵn trong tương lai?
Gul Md Ershad

Câu trả lời:


245

Đúng. Thử:

class USBDevice : GenericDevice, IOurDevice

Lưu ý: Lớp cơ sở nên xuất hiện trước danh sách tên giao diện.

Tất nhiên, bạn vẫn sẽ cần phải thực hiện tất cả các thành viên mà giao diện xác định. Tuy nhiên, nếu lớp cơ sở chứa thành viên khớp với thành viên giao diện, thành viên lớp cơ sở có thể hoạt động như việc triển khai thành viên giao diện và bạn không bắt buộc phải thực hiện lại thủ công.


1
Yup này hoạt động! Tại sao tôi không nghĩ về điều đó! Và để ý kiến ​​dưới đây. Cảm ơn bạn đã giải thích cho tôi về điều đó (Các lớp không kế thừa giao diện, giao diện THỰC HIỆN)
PICyourBrain

1
@Jordan, cũng lưu ý rằng lớp cơ sở và danh sách các giao diện được kế thừa được phân tách bằng dấu phẩy sau dấu hai chấm ban đầu (ví dụ của @ Mehrdad).
JMD

1
để mở rộng chỉ một chút: nếu lớp cơ sở của bạn thực hiện giao diện thì lớp dẫn xuất của bạn sẽ tự động thực hiện giao diện đó - ngay cả khi không có USBDevice : IOurDevice. Thêm việc thực hiện rõ ràng không ảnh hưởng đến lớp cơ sở, nhưng nó có thể giúp nhấn mạnh vào giao diện.
STW

1
@David, trong khi bạn không sai, đó không phải là thuật ngữ ngăn mã của @ Jordan hoạt động. Đó là cú pháp không chính xác.
JMD

2
+1 để xóa một câu hỏi tôi muốn hỏi về các thành viên giống hệt nhau trong cả Cơ sở và Giao diện.
Riegardt Steyn

21

Không, không chính xác. Nhưng nó có thể kế thừa từ một lớp và thực hiện một hoặc nhiều giao diện.

Thuật ngữ rõ ràng là quan trọng khi thảo luận về các khái niệm như thế này. Một trong những điều mà bạn sẽ thấy đánh dấu bài viết của Jon Skeet, ví dụ, cả ở đây và in, là anh ấy luôn chính xác trong cách anh ấy giải mã mọi thứ.


8
Hoặc Eric Lippert, người viết rất chính xác.
Mathias

21

Không liên quan đến câu hỏi (câu trả lời của Mehrdad sẽ giúp bạn tiếp tục) và tôi hy vọng điều này không được coi là khó chịu: các lớp không kế thừa giao diện, họ thực hiện chúng.

.NET không hỗ trợ đa kế thừa, vì vậy việc giữ các điều khoản thẳng có thể giúp ích trong giao tiếp. Một lớp có thể kế thừa từ một siêu lớp và có thể thực hiện nhiều giao diện như nó muốn.


Đáp lại bình luận của Eric ... Tôi đã thảo luận với một nhà phát triển khác về việc các giao diện "kế thừa", "thực hiện", "yêu cầu" hay "mang theo" với một tuyên bố như:

public interface ITwo : IOne

Câu trả lời kỹ thuật là ITwokhông kế thừa IOnevì một vài lý do:

  • Các giao diện không bao giờ có triển khai, vì vậy lập luận rằng ITwo việc thực hiện IOne là sai
  • ITwokế thừa IOnecác phương thức, nếu MethodOne()tồn tại trên IOnethì nó cũng được tích lũy từ ITwo. tức là: ((ITwo)someObject).MethodOne())hợp lệ, mặc dù ITwokhông rõ ràng chứa định nghĩa choMethodOne()
  • ... bởi vì thời gian chạy nói như vậy! typeof(IOne).IsAssignableFrom(typeof(ITwo))trả lạitrue

Cuối cùng chúng tôi đã đồng ý rằng các giao diện hỗ trợ kế thừa thực / đầy đủ. Các tính năng thừa kế bị thiếu (chẳng hạn như ghi đè, truy cập trừu tượng / ảo, v.v.) bị thiếu từ các giao diện, không phải từ kế thừa giao diện. Nó vẫn không làm cho khái niệm trở nên đơn giản hoặc rõ ràng, nhưng nó giúp hiểu những gì thực sự diễn ra trong thế giới của Eric :-)


3
Mặc dù, không may, giao diện kế thừa các giao diện khác. Tôi thấy rằng sự lựa chọn từ không may, nhưng chúng ta đang bị mắc kẹt với nó. Tôi thích nghĩ về các giao diện như yêu cầu các giao diện khác. Đó là, khi bạn nói "giao diện IFoo: IBar" có nghĩa là "người triển khai IFoo cũng được yêu cầu thực hiện IBar".
Eric Lippert

@Eric Tôi đồng ý rằng đó là một thuật ngữ không rõ ràng và đã tranh luận nó với một đồng nghiệp một thời gian trước. Cuối cùng, chúng tôi quyết định rằng "ITwo thừa hưởng IOne". Tôi sẽ cập nhật câu trả lời của mình với một vài lý do nhỏ (chỉ vì chúng không phù hợp rõ ràng trong một bình luận).
STW

Chắc chắn rồi; nếu bạn định nghĩa "A kế thừa từ B" có nghĩa là "các thành viên của B đều là thành viên của A", thì các giao diện sẽ "kế thừa" từ các giao diện cơ sở. Đây là một định nghĩa hợp lý. Nhưng tôi thích nghĩ về "sự kế thừa" vì nghiêm túc hơn về việc không chỉ chia sẻ các thành viên trừu tượng, chưa thực hiện, mà là về việc kế thừa các triển khai . Vì các giao diện không có triển khai, tôi thấy hơi khó chịu khi nghĩ các giao diện là kế thừa từ bất cứ thứ gì. Đó là một điểm tinh tế và gây tranh cãi.
Eric Lippert

Một thuật ngữ khác tôi thích là "mở rộng". Vì vậy, bạn có thể đọc "giao diện IFoo: IBar" có nghĩa là IFoo mở rộng các yêu cầu của IBar.
jasonh

1
@Joan: bạn đúng rằng các lớp thực hiện các giao diện (và không thể kế thừa). Điểm mà Eric đưa ra là các giao diện có thể kế thừa các giao diện khác - có thể hơi khó tiêu hóa do các giao diện chỉ là "thông số kỹ thuật" chứ không phải là triển khai.
STW

1

Tôi tìm thấy câu trả lời cho phần thứ hai của câu hỏi của tôi. Có, một lớp có thể thực hiện một giao diện trong một lớp khác miễn là giao diện đó được khai báo là công khai.


Nó không phải là công khai trong mọi trường hợp. Chỉ cần truy cập. Ví dụ class ContainsAll { private interface INested { /* ... */ } private class MyExample : INested { /* ... */ } }, MyExamplelớp thực hiện một giao diện riêng được lồng vào nhau. Trong các ví dụ khác, giao diện lồng nhau (và lớp chứa) có thể internal. Tất cả phụ thuộc vào người cần sử dụng chúng và bận tâm với chúng.
Jeppe Stig Nielsen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.