ใน C # คลาสสามารถสืบทอดจากคลาสอื่นและอินเตอร์เฟสได้หรือไม่?


130

ฉันต้องการทราบว่าคลาสสามารถสืบทอดจากคลาสและส่วนต่อประสานได้หรือไม่ ตัวอย่างโค้ดด้านล่างใช้งานไม่ได้ แต่ฉันคิดว่ามันบ่งบอกถึงสิ่งที่ฉันต้องการจะทำ เหตุผลที่ฉันต้องการทำเช่นนี้เป็นเพราะ บริษัท ของฉันทำ USB, ซีเรียล, อีเธอร์เน็ตและอุปกรณ์อื่น ๆ ฉันกำลังพยายามพัฒนาส่วนประกอบ / อินเตอร์เฟสทั่วไปที่ฉันสามารถใช้เพื่อเขียนโปรแกรมสำหรับอุปกรณ์ทั้งหมดของเราที่จะช่วยให้สิ่งต่าง ๆ (เช่นการเชื่อมต่อการตัดการเชื่อมต่อการรับเฟิร์มแวร์) เหมือนกันสำหรับแอปพลิเคชันทั้งหมดของเรา

เพื่อเพิ่มคำถามนี้: ถ้า GenericDevice อยู่ในโครงการที่แตกต่างกันฉันสามารถใส่อินเทอร์เฟซ IOurDevices ในโครงการนั้นจากนั้นให้คลาส USBDevice ใช้อินเทอร์เฟซถ้าฉันเพิ่มการอ้างอิงถึงโครงการแรกหรือไม่ เพราะต้องการอ้างอิงเพียงหนึ่งโครงการจากนั้นใช้อินเทอร์เฟซที่แตกต่างกันขึ้นอยู่กับว่าอุปกรณ์คืออะไร

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
สำหรับการอ้างอิงในอนาคตของคุณส่วนที่ 10.1.4 ของข้อกำหนด C # อธิบายถึงวิธีการประกาศคลาสที่มีหลายประเภทฐานอย่างแม่นยำ
Eric Lippert

@Eric Lippert: คุณช่วยฉันเข้าใจเรื่องนี้ได้ไหมว่ามันเป็นวิธีที่ถูกต้องและจะมีในอนาคตหรือไม่?
Gul Md Ershad

คำตอบ:


246

ใช่. ลอง:

class USBDevice : GenericDevice, IOurDevice

หมายเหตุ:คลาสฐานควรมาก่อนรายการชื่ออินเตอร์เฟส

แน่นอนว่าคุณจะต้องใช้สมาชิกทั้งหมดที่อินเตอร์เฟสกำหนดไว้ อย่างไรก็ตามถ้าคลาสฐานมีสมาชิกที่ตรงกับสมาชิกอินเทอร์เฟซสมาชิกคลาสพื้นฐานสามารถทำงานเป็นการใช้งานของสมาชิกอินเทอร์เฟซและคุณไม่จำเป็นต้องใช้มันด้วยตนเองอีกครั้ง


1
ใช่งานนี้! ทำไมฉันไม่คิดอย่างนั้น! และจะแสดงความคิดเห็นด้านล่าง ขอขอบคุณที่เคลียร์ฉันในเรื่องนั้น (คลาสไม่ได้รับส่วนต่อประสาน, ส่วนต่อประสานการนำเข้า)
PICyourBrain

1
@Jordan โปรดทราบว่าคลาสพื้นฐานและรายการอินเตอร์เฟสที่สืบทอดมาจะถูกคั่นด้วยเครื่องหมายจุลภาคหลังจากโคลอนเริ่มต้น (ตัวอย่างของ @ Mehrdad)
JMD

1
เพื่อขยายเพียงเล็ก ๆ น้อย ๆ ถ้าชั้นฐานการดำเนินการของอินเตอร์เฟซที่แล้วระดับของคุณได้รับการดำเนินการโดยอัตโนมัติว่าอินเตอร์เฟซ - USBDevice : IOurDeviceแม้จะไม่ได้ การเพิ่มการใช้งานอย่างชัดเจนไม่ส่งผลกระทบต่อคลาสพื้นฐาน แต่สามารถช่วยให้ความสำคัญกับอินเทอร์เฟซ
STW

1
@ David ในขณะที่คุณไม่ผิดมันไม่ใช่ศัพท์ที่ทำให้ @ Jordan ไม่สามารถทำงานได้ มันเป็นไวยากรณ์ที่ไม่ถูกต้อง
JMD

2
+1 สำหรับการล้างคำถามที่ฉันต้องการถามเกี่ยวกับสมาชิกที่เหมือนกันทั้งในส่วนฐานและส่วนต่อประสาน
Riegardt Steyn

21

ไม่ไม่แน่นอน แต่มันสามารถสืบทอดจากคลาสและใช้หนึ่งอินเตอร์เฟสขึ้นไป

คำศัพท์ที่ชัดเจนมีความสำคัญเมื่อพูดถึงแนวคิดเช่นนี้ หนึ่งในสิ่งที่คุณจะเห็นได้ชัดเจนคืองานเขียนของ Jon Skeet ทั้งที่นี่และที่พิมพ์ออกมาคือเขามีความแม่นยำเสมอในวิธีที่เขาแยกแยะสิ่งต่าง ๆ


8
หรือ Eric Lippert ซึ่งการเขียนนั้นแม่นยำมาก
งัด

21

ที่ไม่เกี่ยวข้องกับคำถาม (คำตอบ Mehrdad ควรได้รับคุณไป) และฉันหวังว่านี้จะไม่ถูกนำมาเป็น nitpicky: เรียนไม่ได้สืบทอดการเชื่อมต่อพวกเขาใช้พวกเขา

.NET ไม่สนับสนุนการสืบทอดหลายแบบดังนั้นการรักษาเงื่อนไขให้ตรงสามารถช่วยในการสื่อสารได้ คลาสสามารถสืบทอดจากซูเปอร์คลาสหนึ่งและสามารถใช้อินเทอร์เฟซได้มากเท่าที่ต้องการ


เพื่อตอบสนองต่อความคิดเห็นของ Eric ... ฉันได้พูดคุยกับผู้พัฒนารายอื่นเกี่ยวกับว่าอินเตอร์เฟส "รับช่วง", "นำไปใช้", "ต้องการ" หรืออินเทอร์เฟซ "นำมาด้วย" พร้อมกับประกาศ:

public interface ITwo : IOne

คำตอบทางเทคนิคคือการITwoสืบทอดIOneด้วยเหตุผลสองสามประการ:

  • อินเทอร์เฟซไม่เคยมีการนำไปใช้ดังนั้นการพิสูจน์ว่าITwo การดำเนินการ IOneนั้นผิดพลาด
  • ITwoสืบทอดIOneวิธีการถ้าMethodOne()อยู่บนIOneนั้นก็ยังเป็น accesible ITwoจาก เช่น: ((ITwo)someObject).MethodOne())ถูกต้องแม้ว่าจะITwoไม่ได้มีคำจำกัดความสำหรับMethodOne()
  • ... เพราะรันไทม์พูดอย่างนั้น! typeof(IOne).IsAssignableFrom(typeof(ITwo))ผลตอบแทนtrue

ในที่สุดเราก็ตกลงกันว่าอินเตอร์เฟสสนับสนุนการสืบทอดจริง / เต็ม คุณลักษณะการสืบทอดที่หายไป (เช่นการแทนที่ผู้เข้าถึงบทคัดย่อ / เสมือน ฯลฯ ) ขาดหายไปจากส่วนต่อประสานไม่ใช่จากส่วนต่อประสานที่สืบทอดมา มันยังไม่ได้ทำให้แนวคิดเรียบง่ายหรือชัดเจน แต่ช่วยให้เข้าใจว่าเกิดอะไรขึ้นภายใต้ประทุนในโลกของ Eric :-)


3
อย่างไรก็ตามโชคไม่ดีที่อินเตอร์เฟสได้รับอินเทอร์เฟซอื่น ๆ ฉันพบว่าตัวเลือกคำไม่ดี แต่ตอนนี้เราติดอยู่กับมันแล้ว ฉันชอบคิดว่าอินเทอร์เฟซที่ต้องการอินเทอร์เฟซอื่น ๆ นั่นคือเมื่อคุณพูดว่า "อินเตอร์เฟส IFoo: IBar" นั่นหมายถึง "ผู้ดำเนินการของ IFoo จำเป็นต้องใช้งาน IBar ด้วย"
Eric Lippert

@Eric ฉันเห็นด้วยว่ามันเป็นคำที่ไม่ชัดเจนและได้ถกเถียงกับเพื่อนร่วมงานของฉันในขณะที่กลับมา ในตอนท้ายเราตัดสินใจว่าการพูดว่า "ITwo สืบทอด IOne" ฉันจะอัปเดตคำตอบของฉันด้วยเหตุผลเล็ก ๆ น้อย ๆ (เพียงเพราะพวกเขาจะไม่พอดีกับความคิดเห็น)
STW

แน่นอนว่า; หากคุณกำหนด "A สืบทอดจาก B" ตามความหมาย "สมาชิกของ B คือสมาชิกทั้งหมดของ A" ดังนั้นอินเตอร์เฟสจะทำ "สืบทอด" จากอินเตอร์เฟสพื้นฐาน นี่คือคำจำกัดความที่สมเหตุสมผล แต่ผมชอบคิดว่า "มรดก" เป็นอย่างเคร่งครัดมากขึ้นเกี่ยวกับการไม่ได้เป็นเพียงการแบ่งปันนามธรรมสมาชิก unimplemented แต่เป็นเรื่องเกี่ยวกับการสืบทอดการใช้งาน เนื่องจากส่วนต่อประสานไม่มีการใช้งานฉันพบว่ามันค่อนข้างน่าแปลกใจที่คิดว่าส่วนต่อประสานนั้นเป็นสิ่งที่สืบทอดมาจากสิ่งใด มันเป็นจุดที่บอบบางและเป็นที่ถกเถียงกัน
Eric Lippert

อีกคำที่ฉันชอบคือ "ขยาย" ดังนั้นคุณสามารถอ่าน "อินเทอร์เฟซ IFoo: IBar" เพื่อหมายความว่า IFoo ขยายความต้องการของ IBar
jasonh

1
@Joan: คุณถูกต้องที่คลาสใช้อินเตอร์เฟส (และไม่สามารถสืบทอด) จุดที่เอริคทำคืออินเทอร์เฟซสามารถรับอินเทอร์เฟซอื่น ๆ ได้ซึ่งอาจย่อยได้ยากเนื่องจากอินเตอร์เฟสเป็นเพียง "ข้อมูลจำเพาะ" มากกว่าการนำไปใช้งาน
STW

1

ฉันพบคำตอบในส่วนที่สองของคำถามของฉัน ใช่คลาสสามารถใช้อินเทอร์เฟซที่อยู่ในคลาสอื่นตราบใดที่อินเทอร์เฟซถูกประกาศเป็นสาธารณะ


ไม่จำเป็นต้องเป็นสาธารณะในทุกกรณี เข้าถึงได้ง่าย ยกตัวอย่างเช่นclass ContainsAll { private interface INested { /* ... */ } private class MyExample : INested { /* ... */ } }ในMyExampleระดับใช้อินเตอร์เฟซส่วนตัวที่ซ้อนกัน ในตัวอย่างอื่นอินเทอร์เฟซที่ซ้อนกัน (และคลาสที่ประกอบด้วย) อาจเป็นinternalได้ ทุกอย่างขึ้นอยู่กับผู้ที่ต้องการใช้และรบกวนพวกเขา
Jeppe Stig Nielsen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.