เหตุใดจึงมีการสืบทอดหลายรายการใน C ++ แต่ไม่ใช่ใน C #
ฉันคิดว่า (โดยไม่มีการอ้างอิงอย่างหนัก) ว่าใน Java พวกเขาต้องการ จำกัด ความหมายของภาษาเพื่อทำให้ภาษาง่ายต่อการเรียนรู้และเนื่องจากรหัสที่ใช้การสืบทอดหลายครั้งนั้นซับซ้อนเกินกว่าจะดีกว่าของตัวเอง และเนื่องจากการสืบทอดหลายอย่างเต็มรูปแบบมีความซับซ้อนในการใช้งานมากขึ้นดังนั้นมันจึงทำให้เครื่องเสมือนง่ายขึ้นเช่นกัน (การสืบทอดหลายครั้งมีปฏิสัมพันธ์โดยเฉพาะอย่างยิ่งกับตัวเก็บขยะเพราะมันต้องรักษาพอยน์เตอร์ไว้กลางวัตถุ )
และเมื่อออกแบบ C # ฉันคิดว่าพวกเขามองที่ Java เห็นว่าการสืบทอดหลายอย่างเต็มรูปแบบนั้นไม่ได้พลาดมากนักและเลือกที่จะรักษาสิ่งต่าง ๆ ให้เรียบง่ายเช่นกัน
C ++ แก้ไขความคลุมเครือของลายเซ็นวิธีที่เหมือนกันซึ่งสืบทอดมาจากคลาสพื้นฐานหลายคลาสได้อย่างไร
มันไม่ได้ มีไวยากรณ์ในการเรียกใช้เมธอดคลาสพื้นฐานจากฐานเฉพาะอย่างชัดเจน แต่ไม่มีวิธีการแทนที่หนึ่งในเมธอดเสมือนเท่านั้นและถ้าคุณไม่แทนที่เมธอดในคลาสย่อยคุณจะไม่สามารถเรียกมันได้โดยไม่ต้องระบุฐาน ชั้น
และทำไมการออกแบบเดียวกันไม่รวมอยู่ใน C #
ไม่มีอะไรจะรวมเป็น
ตั้งแต่ Giorgio พูดถึงวิธีการขยายส่วนต่อประสานในความคิดเห็นฉันจะอธิบายว่ามิกซ์อินคืออะไรและมีการนำไปใช้ในหลายภาษาอย่างไร
ส่วนต่อประสานใน Java และ C # ถูก จำกัด ให้ประกาศวิธีการเท่านั้น แต่วิธีการจะต้องมีการดำเนินการในแต่ละชั้นเรียนที่ได้รับส่วนต่อประสาน อย่างไรก็ตามมีอินเทอร์เฟซขนาดใหญ่ซึ่งมันจะมีประโยชน์ในการเตรียมการใช้งานเริ่มต้นของวิธีการบางอย่างในแง่ของผู้อื่น ตัวอย่างทั่วไปเปรียบได้ (ในภาษาเทียม):
mixin IComparable {
public bool operator<(IComparable r) = 0;
public bool operator>(IComparable r) { return r < this; }
public bool operator<=(IComparable r) { return !(r < this); }
public bool operator>=(IComparable r) { return !(r > this); }
public bool operator==(IComparable r) { return !(r < this) && !(r > this); }
public bool operator!=(IComparable r) { return r < this || r > this; }
};
ความแตกต่างจากคลาสเต็มคือสิ่งนี้ไม่สามารถมีข้อมูลสมาชิกได้ มีหลายตัวเลือกสำหรับการใช้งานนี้ เห็นได้ชัดว่าการสืบทอดหลายรายการเป็นหนึ่ง แต่การรับมรดกหลายรายการค่อนข้างซับซ้อนกว่าที่จะใช้ แต่มันไม่จำเป็นจริงๆที่นี่ แต่หลายภาษาใช้สิ่งนี้โดยแยกมิกซ์อินในอินเทอร์เฟซซึ่งถูกนำไปใช้โดยคลาสและที่เก็บของการใช้งานเมธอดซึ่งถูกแทรกเข้าไปในคลาสนั้นเองหรือสร้างคลาสพื้นฐานระดับกลางและวางไว้ที่นั่น สิ่งนี้ถูกนำไปใช้ใน Ruby และD , จะถูกนำมาใช้ใน Java 8 และสามารถนำไปใช้งานด้วยตนเองใน C ++ โดยใช้รูปแบบเทมเพลตที่เกิดขึ้นซ้ำๆ ข้างต้นในรูปแบบ CRTP ดูเหมือนว่า:
template <typename Derived>
class IComparable {
const Derived &_d() const { return static_cast<const Derived &>(*this); }
public:
bool operator>(const IComparable &r) const { r._d() < _d(); }
bool operator<=(const IComparable &r) const { !(r._d() < _d(); }
...
};
และใช้เหมือน:
class Concrete : public IComparable<Concrete> { ... };
สิ่งนี้ไม่ต้องการอะไรที่จะประกาศเสมือนเป็นคลาสพื้นฐานปกติดังนั้นหากมีการใช้อินเทอร์เฟซในแม่แบบให้เปิดตัวเลือกการปรับให้เหมาะสมที่มีประโยชน์ โปรดทราบว่าใน C ++ สิ่งนี้อาจยังคงสืบทอดมาเป็นพาเรนต์ที่สอง แต่ในภาษาที่ไม่อนุญาตให้มีการสืบทอดหลายรายการจะถูกแทรกลงในเชนการสืบทอดเดี่ยวดังนั้นจึงเป็นมากกว่า
template <typename Derived, typename Base>
class IComparable : public Base { ... };
class Concrete : public IComparable<Concrete, Base> { ... };
การใช้งานคอมไพเลอร์อาจหรือไม่อาจหลีกเลี่ยงการจัดส่งเสมือน
มีการนำการใช้งานอื่นไปใช้ใน C # ใน C # การใช้งานเป็นวิธีการคงที่ของคลาสที่แยกจากกันอย่างสมบูรณ์และไวยากรณ์การโทรวิธีการจะถูกตีความอย่างเหมาะสมโดยคอมไพเลอร์หากวิธีการของชื่อที่กำหนดไม่มีอยู่ แต่ "วิธีการขยาย" ถูกกำหนดไว้ นี่คือข้อดีที่สามารถเพิ่มวิธีการขยายไปยังคลาสที่คอมไพล์แล้วและข้อเสียที่วิธีดังกล่าวไม่สามารถ overriden เช่นเพื่อให้รุ่นที่ปรับให้เหมาะสม