เหตุใดจึงต้องมีการแทนที่คำหลักหน้าวิธีนามธรรมเมื่อเราใช้พวกเขาในชั้นเรียนเด็ก


9

เมื่อเราสร้างคลาสที่สืบทอดมาจากคลาสนามธรรมและเมื่อเราใช้คลาสนามธรรมที่สืบทอดมาทำไมเราต้องใช้คีย์เวิร์ด override?

public abstract class Person
{
    public Person()
    {

    }

    protected virtual void Greet()
    {
        // code
    }

    protected abstract void SayHello();
}

public class Employee : Person
{
    protected override void SayHello() // Why is override keyword necessary here?
    {
        throw new NotImplementedException();
    }

    protected override void Greet()
    {
        base.Greet();
    }
}

เนื่องจากเมธอดถูกประกาศเป็นนามธรรมในคลาสพาเรนต์จึงไม่มีการนำไปใช้ในคลาสพาเรนต์ดังนั้นทำไมคีย์เวิร์ด override จำเป็นที่นี่?


2
วิธีนามธรรมหมายถึงวิธีเสมือนโดยนัยต่อรายละเอียด
Pavel Anikhouski


"ตัวแก้ไขการแทนที่จำเป็นต้องใช้เพื่อขยายหรือแก้ไขการนำนามธรรมหรือเสมือนจริงของวิธีการ, คุณสมบัติ, ตัวทำดัชนีหรือเหตุการณ์ที่สืบทอดมา" docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
gunr2171

2
เพราะถ้าคุณไม่ใส่มันไว้คุณกำลัง "ซ่อน" วิธีการแทนการเอาชนะมัน นั่นเป็นเหตุผลที่คุณได้รับคำเตือนว่า "ถ้านั่นคือสิ่งที่คุณตั้งใจไว้ให้ใช้คำnewหลัก" ... คุณจะได้รับข้อผิดพลาด "ไม่มีวิธีการแทนที่วิธีการเรียนระดับฐาน" #
Ron Beyer

@ RonBeyer ด้วยวิธีเสมือนจริงใช่ แต่ด้วยนามธรรมมันก็จะไม่รวบรวม
Johnathan Barclay

คำตอบ:


14

เมื่อเราสร้างคลาสที่สืบทอดมาจากคลาสนามธรรมและเมื่อเราใช้คลาสนามธรรมที่สืบทอดมาทำไมเราต้องใช้คีย์เวิร์ด override?

"ทำไม?" คำถามเช่นนี้อาจตอบยากเพราะมันคลุมเครือ ฉันจะสมมติว่าคำถามของคุณคือ "มีข้อโต้แย้งอะไรบ้างในระหว่างการออกแบบภาษาเพื่อโต้แย้งสำหรับตำแหน่งที่จำเป็นต้องใช้overrideคำหลัก"

มาเริ่มกันที่ขั้นตอนก่อนกัน ในบางภาษาพูดว่า Java วิธีการเป็นเสมือนโดยค่าเริ่มต้นและแทนที่โดยอัตโนมัติ นักออกแบบของ C # ทราบเรื่องนี้และคิดว่าเป็นข้อบกพร่องเล็กน้อยใน Java C # ไม่ใช่ "Java ที่เอาชิ้นส่วนที่โง่ออกมา" อย่างที่บางคนกล่าว แต่ผู้ออกแบบ C # กระตือรือร้นที่จะเรียนรู้จากประเด็นการออกแบบที่เป็นปัญหาของ C, C ++ และ Java และไม่ทำซ้ำใน C #

นักออกแบบ C # ถือว่าการเอาชนะเป็นแหล่งที่เป็นไปได้ของข้อบกพร่อง; ท้ายที่สุดมันเป็นวิธีที่จะเปลี่ยนพฤติกรรมของรหัสที่มีอยู่ทดสอบและที่เป็นอันตราย การเอาชนะไม่ใช่สิ่งที่ควรทำอย่างตั้งใจหรือไม่ตั้งใจ ควรได้รับการออกแบบโดยคนที่คิดอย่างหนักเกี่ยวกับเรื่องนี้ นั่นเป็นสาเหตุที่วิธีการไม่ใช่ค่าเริ่มต้นเสมือนจริงและทำไมคุณต้องบอกว่าคุณกำลังเอาชนะวิธีการ

นั่นคือเหตุผลพื้นฐาน ตอนนี้เราสามารถไปสู่การใช้เหตุผลขั้นสูงมากขึ้น

คำตอบของ StriplingWarrior ให้การตัดแรกที่ดีที่ทำให้การโต้แย้งขั้นสูงมากขึ้น ผู้เขียนคลาสที่ได้รับอาจจะไม่รู้เกี่ยวกับระดับฐานอาจจะตั้งใจที่จะทำให้วิธีการใหม่และเราไม่ควรอนุญาตให้ผู้ใช้เพื่อแทนที่โดยไม่ได้ตั้งใจ

แม้ว่าประเด็นนี้จะสมเหตุสมผล แต่มีหลายข้อโต้แย้งเช่น:

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

จากนั้นเราจะสร้างข้อโต้แย้งที่ก้าวหน้ายิ่งขึ้นในประเด็นนี้ ผู้เขียนของคลาสที่ได้รับภายใต้สถานการณ์ใดสามารถแก้ตัวโดยไม่รู้ว่าคลาสพื้นฐานทำอะไร? ดีพิจารณาสถานการณ์นี้:

  • ผู้เขียนชั้นฐานทำให้ชั้นฐานนามธรรม
  • ผู้เขียนคลาสที่ได้รับในทีมอื่นทำให้คลาส D ที่ได้รับมามีเมธอด M
  • ผู้เขียนคลาสฐานตระหนักดีว่าทีมที่ขยายคลาส B นั้นจะต้องจัดหาเมธอด M เสมอดังนั้นผู้เขียนคลาสฐานจะเพิ่มเมธอด abstract M
  • เมื่อคลาส D ถูกคอมไพล์ใหม่จะเกิดอะไรขึ้น

สิ่งที่เราต้องการให้เกิดขึ้นเป็นผู้เขียนของ D จะได้รับแจ้งว่ามีบางอย่างที่เกี่ยวข้องมีการเปลี่ยนแปลง สิ่งที่เกี่ยวข้องที่มีการเปลี่ยนแปลงคือตอนนี้ M เป็นข้อกำหนดและการใช้งานของพวกเขาจะต้องมากเกินไป DM อาจจำเป็นต้องเปลี่ยนพฤติกรรมเมื่อเรารู้ว่าสามารถเรียกได้จากคลาสฐาน สิ่งที่ถูกต้องคืออย่าพูดอย่างเงียบ ๆ ว่า "โอ้มี DM แล้วขยาย BM" สิ่งที่ถูกต้องสำหรับคอมไพเลอร์ที่ต้องทำคือล้มเหลวและพูดว่า "เฮ้, ผู้เขียน D, ตรวจสอบสมมติฐานของคุณซึ่งไม่สามารถใช้งานได้อีกต่อไปและแก้ไขรหัสของคุณหากจำเป็น"

ในตัวอย่างของคุณสมมติว่าoverrideเป็นตัวเลือกที่เปิดSayHelloเพราะมันเป็นวิธีที่เป็นนามธรรม มีความเป็นไปได้สองประการ: (1) ผู้เขียนโค้ดตั้งใจที่จะลบล้างวิธีนามธรรมหรือ (2) วิธีการแทนที่นั้นจะถูกลบล้างโดยไม่ได้ตั้งใจเพราะมีคนอื่นเปลี่ยนคลาสพื้นฐาน เราไม่สามารถบอกความเป็นไปได้เหล่านี้ออกจากกันถ้าoverrideเป็นตัวเลือก

แต่ถ้าoverrideถูกต้องแล้วเราสามารถบอกความแตกต่างสามสถานการณ์ หากมีข้อผิดพลาดที่เป็นไปได้ในรหัสนั้นoverrideจะหายไป หากมีการจงใจเอาชนะแล้วoverrideเป็นปัจจุบัน และถ้าหากจงใจไม่ล้มล้างก็newจะปรากฏขึ้น การออกแบบของ C # ทำให้เราสามารถสร้างความแตกต่างที่ลึกซึ้งเหล่านี้ได้

โปรดจำไว้รวบรวมข้อผิดพลาดการรายงานต้องอ่านใจของนักพัฒนา ; คอมไพเลอร์จะต้องอนุมานจากรหัสผิดสิ่งที่รหัสที่ถูกต้องที่ผู้เขียนมีอยู่ในใจและให้ข้อผิดพลาดที่ชี้ให้พวกเขาไปในทิศทางที่ถูกต้อง เบาะแสเพิ่มเติมที่เราสามารถทำให้นักพัฒนาทิ้งไว้ในโค้ดเกี่ยวกับสิ่งที่พวกเขากำลังคิดอยู่ยิ่งงานที่คอมไพเลอร์สามารถทำได้ดีกว่าในการรายงานข้อผิดพลาดดังนั้นยิ่งคุณสามารถค้นหาและแก้ไขข้อบกพร่องได้เร็วขึ้นเท่านั้น

แต่โดยทั่วไป, C # ได้รับการออกแบบสำหรับโลกที่เปลี่ยนแปลงรหัส คุณลักษณะที่ยอดเยี่ยมมากมายของ C # ซึ่งปรากฏว่า "แปลก" นั้นมีอยู่จริงเพราะพวกเขาแจ้งให้ผู้พัฒนาทราบเมื่อข้อสันนิษฐานที่เคยใช้นั้นไม่ถูกต้องเพราะชั้นฐานเปลี่ยนไป คลาสของบั๊กนี้เรียกว่า "ความล้มเหลวของคลาสพื้นฐานที่เปราะ" และ C # มีจำนวนการบรรเทาที่น่าสนใจสำหรับคลาสความล้มเหลวนี้


4
ขอบคุณที่ให้รายละเอียด ฉันมักจะขอบคุณคำตอบของคุณทั้งคู่เพราะมันเป็นการดีที่จะได้รับเสียงที่เชื่อถือได้จากใครบางคน "ข้างใน" และเพราะคุณทำงานที่ยอดเยี่ยมในการอธิบายสิ่งต่าง ๆ ได้อย่างเรียบง่ายและสมบูรณ์แบบ
StriplingWarrior

ขอบคุณสำหรับคำอธิบายในเชิงลึก! ขอบคุณมันจริงๆ
psj01

คะแนนที่ดี! คุณช่วยรายการบรรเทาอื่น ๆ ที่คุณพูดถึงได้บ้างไหม?
aksh1618

3

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

ในขณะที่มันเป็นความจริงที่ว่าวิธีการที่เป็นนามธรรมจะต้องถูกแทนที่ในชั้นเรียนของเด็กที่ไม่ใช่นามธรรม, crafters ของ C # อาจรู้สึกว่ามันยังดีกว่าที่จะชัดเจนเกี่ยวกับสิ่งที่คุณพยายามที่จะทำ


นี่เป็นจุดเริ่มต้นที่ดีในการทำความเข้าใจเหตุผลของทีมออกแบบภาษา ฉันได้เพิ่มคำตอบซึ่งแสดงให้เห็นว่าทีมเริ่มต้นจากแนวคิดที่คุณแสดงไว้ที่นี่ได้อย่างไร
Eric Lippert

1

เนื่องจากabstractmethod เป็นวิธีเสมือนโดยไม่มีการใช้งานตามข้อกำหนดภาษา C # หมายความว่าวิธีนามธรรมเป็นวิธีเสมือนโดยปริยาย และoverrideใช้เพื่อขยายหรือปรับเปลี่ยนการใช้นามธรรมหรือเสมือนจริงอย่างที่คุณเห็นที่นี่

ในการใช้ถ้อยคำใหม่อีกครั้งเล็กน้อย - คุณใช้วิธีการเสมือนเพื่อใช้การรวมแบบล่าช้าบางส่วนในขณะที่วิธีการแบบนามธรรมบังคับคลาสย่อยของประเภทให้มีวิธีการแทนที่อย่างชัดเจน นั่นคือประเด็นเมื่อเมธอดคือvirtualมันสามารถถูกแทนที่ได้เมื่อมันabstract- มันจะต้องถูกแทนที่


1
ฉันคิดว่าคำถามไม่ได้เกี่ยวกับสิ่งที่มันทำ แต่ทำไมต้องชัดเจน
Johnathan Barclay

0

หากต้องการเพิ่มคำตอบของ @ StriplingWarrior ฉันคิดว่ามันถูกสร้างขึ้นเพื่อให้มีไวยากรณ์ที่สอดคล้องกับการแทนที่วิธีเสมือนในคลาสพื้นฐาน

public abstract class MyBase
{
    public virtual void MyVirtualMethod() { }

    public virtual void MyOtherVirtualMethod() { }

    public abstract void MyAbtractMethod();
}

public class MyDerived : MyBase
{
    // When overriding a virtual method in MyBase, we use the override keyword.
    public override void MyVirtualMethod() { }

    // If we want to hide the virtual method in MyBase, we use the new keyword.
    public new void MyOtherVirtualMethod() { }

    // Because MyAbtractMethod is abstract in MyBase, we have to override it: 
    // we can't hide it with new.
    // For consistency with overriding a virtual method, we also use the override keyword.
    public override void MyAbtractMethod() { }
}

ดังนั้น C # อาจถูกออกแบบมาเพื่อที่คุณไม่จำเป็นต้องใช้คีย์เวิร์ด override สำหรับการแทนที่เมธอด abstract แต่ฉันคิดว่านักออกแบบตัดสินใจที่จะสับสนเพราะมันจะไม่สอดคล้องกับ overriding วิธีเสมือนจริง


Re: "นักออกแบบตัดสินใจที่จะทำให้สับสนเพราะมันจะไม่สอดคล้องกับการเอาชนะวิธีเสมือน" - ใช่ แต่มากกว่านั้น สมมติว่าคุณมีคลาสฐาน B พร้อมเมธอดเสมือน M และคลาส D ที่ได้รับพร้อมการแทนที่ ทีนี้สมมุติว่าผู้เขียน B ตัดสินใจสร้าง M นามธรรม นั่นเป็นการเปลี่ยนแปลงที่รุนแรง แต่บางทีพวกเขาก็ทำเช่นนั้น คำถาม: ผู้เขียน D ควรถูกลบออกoverrideหรือไม่ ฉันคิดว่าคนส่วนใหญ่จะยอมรับว่ามันเป็นเรื่องไร้สาระที่จะบังคับให้ผู้เขียน D ทำการเปลี่ยนแปลงรหัสที่ไม่จำเป็น ชั้นเรียนของพวกเขาโอเค!
Eric Lippert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.