ทำไมวิธีการแบบคงที่ไม่สามารถเขียนทับได้?


13

ในการตอบคำถามนี้ฉันทามติทั่วไปคือว่าวิธีการแบบสแตติกไม่ได้หมายความว่าจะถูกเขียนทับ (และฟังก์ชันแบบคงที่ใน C # ไม่สามารถเสมือนหรือนามธรรม) นี่ไม่ใช่เฉพาะกรณีใน C # เท่านั้น Java ยังห้ามสิ่งนี้และ C ++ ดูเหมือนจะไม่ชอบเช่นกัน อย่างไรก็ตามฉันสามารถนึกถึงตัวอย่างของฟังก์ชั่นคงที่ที่ฉันต้องการแทนที่ในระดับเด็ก (ตัวอย่างเช่นวิธีการจากโรงงาน) ในทางทฤษฎีมีวิธีการรอบพวกเขาไม่มีพวกเขาสะอาดหรือเรียบง่าย

ทำไมฟังก์ชั่นคงที่ไม่สามารถเขียนทับได้?


4
Delphi สนับสนุนวิธีการเรียนซึ่งค่อนข้างคล้ายกับวิธีการแบบคงที่และสามารถแทนที่ พวกเขาได้รับselfตัวชี้ที่ชี้ไปยังคลาสและไม่ใช่อินสแตนซ์ของคลาส
CodesInChaos

นิยามของสถิตคือหนึ่ง: ขาดการเปลี่ยนแปลง ฉันอยากรู้ว่าทำไมคุณทำฟังก์ชั่นคงที่ที่คุณต้องการแทนที่
JeffO

4
@JeffO: นิยามภาษาอังกฤษของ "static" นั้นไม่มีอะไรเกี่ยวข้องกับซีแมนทิกเฉพาะของฟังก์ชันสมาชิกแบบคงที่
Lightness Races ที่ Orbit

5
คำถามของคุณคือ "ทำไมวิธีการแบบคงที่ควรใช้การจัดส่งแบบคงที่แทนการส่งแบบเสมือนเดียว ?" เมื่อคุณพูดถึงคำถามที่ฉันคิดว่ามันตอบตัวเอง
Eric Lippert

1
@EricLippert คำถามน่าจะดีกว่าในประโยค "ทำไม C # เสนอวิธีการคงที่แทนที่จะเป็นวิธีการเรียน?" แต่ก็ยังคงเป็นคำถามที่ถูกต้อง
CodesInChaos

คำตอบ:


13

ด้วยวิธีการคงที่ไม่มีวัตถุที่จะให้การควบคุมที่เหมาะสมของกลไกการแทนที่

กลไกวิธีเสมือนคลาส / อินสแตนซ์ปกติช่วยให้สามารถควบคุมการแทนที่อย่างละเอียดได้ดังนี้: แต่ละวัตถุจริงเป็นตัวอย่างของคลาสเดียวกัน คลาสนั้นเป็นตัวกำหนดพฤติกรรมของการแทนที่ มันจะได้รับรอยแตกแรกที่วิธีการเสมือน จากนั้นสามารถเลือกที่จะเรียกวิธีผู้ปกครองในเวลาที่เหมาะสมสำหรับการนำไปใช้งาน แต่ละวิธีหลักก็จะได้รับการเปิดเพื่อเรียกวิธีการของมัน สิ่งนี้ส่งผลให้เกิดการเชิญชวนของ parent ที่ดีซึ่งเป็นหนึ่งในแนวคิดของการใช้รหัสซ้ำที่ทำให้เกิดการวางแนวของวัตถุ (นี่คือรหัสฐาน / คลาสของซุปเปอร์กำลังถูกนำมาใช้ใหม่ในวิธีที่ค่อนข้างซับซ้อนอีกแนวคิดหนึ่งของการใช้รหัสใน orthogonal อีกครั้งใน OOP ก็คือมีวัตถุหลายอย่างในคลาสเดียวกัน)

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

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

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

การแทนที่สถิตยศาสตร์จะไม่เป็นระเบียบมากและสิ่งต่าง ๆ เช่นนี้เคยทำมาก่อน

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

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

นี่ไม่ได้บอกว่ามันเป็นไปไม่ได้ที่จะสร้างกลไกสำหรับการแทนที่ของสถิต แต่สิ่งที่ฉันอยากจะทำแทนคือการเปลี่ยนฟิลด์คงที่และวิธีการคงที่เป็นอินสแตนซ์ฟิลด์และวิธีการของอินสแตนซ์ของ metaclasses เทคนิคการปฐมนิเทศจะนำไปใช้ โปรดทราบว่ามีระบบที่ทำสิ่งนี้เช่นกัน: CSE 341: คลาส Smalltalk และ metaclasses ; ดูเพิ่มเติม: อะไรคือ Smalltalk ที่เทียบเท่ากับสแตติกของ Java?


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

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


ถ้าฉันเข้าใจถูกต้อง: มันไม่สำคัญว่าคุณจะหรือควรจะทำอะไรแบบนี้ในทางทฤษฎี แต่มากกว่านั้นโดยทางโปรแกรมการใช้มันจะยากและไม่จำเป็นต้องมีประโยชน์หรือไม่?
PixelArtDragon

@ การันดูภาคผนวกของฉัน
Erik Eidt

1
ฉันไม่ซื้ออาร์กิวเมนต์การสั่งซื้อ คุณได้รับวิธีการเรียนที่คุณเรียกว่าวิธีการใน (หรือ superclass แรกที่ให้วิธีการตาม linearization เลือกภาษาของคุณ)
ฮอบส์

1
@PierreArlaud: แต่this.instanceMethod()มีความละเอียดแบบไดนามิกดังนั้นหากself.staticMethod()มีความละเอียดเดียวกันคือแบบไดนามิกมันจะไม่เป็นวิธีคงที่อีกต่อไป
Jörg W Mittag

1
จำเป็นต้องเพิ่มซีแมนทิกส์สำหรับวิธีการแบบคงที่ Java + metaclasses สมมุติไม่จำเป็นต้องเพิ่มอะไรอีก! คุณเพียงแค่สร้างคลาสของวัตถุและวิธีการแบบสแตติกก็จะกลายเป็นวิธีการแบบอินสแตนซ์ปกติ คุณไม่จำเป็นต้องเพิ่มบางสิ่งลงในภาษาเนื่องจากคุณใช้แนวคิดที่มีอยู่แล้วเท่านั้น: วัตถุคลาสและวิธีการอินสแตนซ์ ในฐานะโบนัสคุณจะต้องลบวิธีการแบบคงที่ออกจากภาษา! คุณสามารถเพิ่มคุณสมบัติได้โดยลบแนวคิดและทำให้ความซับซ้อนของภาษานั้นออกไป!
Jörg W Mittag

9

การเอาชนะขึ้นอยู่กับการแจกจ่ายเสมือน: คุณใช้ชนิดรันไทม์ของthisพารามิเตอร์เพื่อตัดสินใจว่าจะเรียกวิธีใด วิธีการคงที่ไม่มีthisพารามิเตอร์ดังนั้นจึงไม่มีอะไรที่จะส่ง

บางภาษาสะดุดตา Delphi และ Python มีขอบเขต "ในระหว่าง" ที่ช่วยให้นี้: วิธีการเรียน วิธีการเรียนไม่ได้เป็นวิธีการอินสแตนซ์สามัญ แต่มันก็ไม่คงที่เช่นกัน มันได้รับselfพารามิเตอร์ (น่าแปลกใจที่ทั้งสองภาษาเรียกthisพารามิเตอร์self) ซึ่งเป็นการอ้างอิงถึงชนิดของวัตถุเองแทนที่จะเป็นอินสแตนซ์ของประเภทนั้น ด้วยค่านั้นตอนนี้คุณมีชนิดที่พร้อมใช้งานเพื่อทำการส่งแบบเสมือน

น่าเสียดายที่ทั้ง JVM และ CLR ไม่มีอะไรที่เทียบเคียงได้


มันเป็นภาษาที่ใช้selfที่มีคลาสเป็นของจริง, วัตถุอินสแตนซ์ด้วยวิธี overridable - แน่นอนว่า Smalltalk, Ruby, Dart, Objective C, Self, Python? เปรียบเทียบกับภาษาที่ใช้หลัง C ++ ที่คลาสไม่ได้เป็นออบเจ็กต์ชั้นหนึ่งแม้ว่าจะมีการเข้าถึงแบบสะท้อนหรือไม่
Jerry101

เพื่อให้ชัดเจนคุณกำลังบอกว่าใน Python หรือ Delphi คุณสามารถแทนที่วิธีการเรียน?
Erik Eidt

@ErikEidt ใน Python ทุกอย่างสวยมากสามารถเขียนทับได้ ใน Delphi วิธีการเรียนสามารถประกาศอย่างชัดเจนvirtualแล้วแทนที่ในชั้นเรียนสืบทอดเช่นเดียวกับวิธีการตัวอย่าง
Mason Wheeler

ดังนั้นภาษาเหล่านี้ให้แนวคิดเกี่ยวกับ metaclasses บางอย่างใช่มั้ย
Erik Eidt

1
@ErikEidt Python มี metaclasses "จริง" ใน Delphi การอ้างอิงคลาสเป็นชนิดข้อมูลพิเศษไม่ใช่คลาสในตัวมันเอง
Mason Wheeler

3

คุณถาม

ทำไมฟังก์ชั่นคงที่ไม่สามารถเขียนทับได้?

ฉันถาม

ทำไมฟังก์ชั่นที่คุณต้องการจะลบล้างเป็นแบบคงที่?

บางภาษาบังคับให้คุณเริ่มการแสดงด้วยวิธีคงที่ แต่หลังจากนั้นคุณสามารถแก้ปัญหาได้มากมายโดยไม่ต้องใช้วิธีการคงที่อีกต่อไป

บางคนชอบใช้วิธีการคงที่ทุกเวลาไม่มีการพึ่งพาสถานะในวัตถุ บางคนชอบใช้วิธีการคงที่สำหรับการสร้างวัตถุอื่น ๆ บางคนชอบหลีกเลี่ยงวิธีการคงที่มากที่สุด

ไม่มีคนเหล่านี้ผิด

หากคุณต้องการให้ overridable เพียงหยุดการติดฉลากแบบคงที่ สิ่งที่จะทำลายเพราะคุณมีวัตถุไร้สัญชาติที่บินไปมา


หากภาษารองรับ structs แล้วประเภทของหน่วยสามารถเป็น struct ขนาดศูนย์ซึ่งถูกส่งผ่านไปยังวิธีการเข้าตรรกะ การสร้างวัตถุนั้นเป็นรายละเอียดการใช้งาน และถ้าเราเริ่มพูดถึงรายละเอียดการใช้งานจริงตามความเป็นจริงฉันคิดว่าคุณต้องพิจารณาด้วยว่าในการใช้งานจริงโลกประเภท zero-size ไม่จำเป็นต้องถูกยกตัวอย่างจริง (เพราะไม่มีคำแนะนำในการโหลด มันหรือเก็บไว้)
Theodoros Chatzigiannakis

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

ฉันคิดว่ามันขึ้นอยู่กับว่าคุณจะมองอย่างไร ตัวอย่างเช่นเมื่อโปรแกรมเริ่มทำงานระบบปฏิบัติการจะโหลดลงในหน่วยความจำแล้วไปยังที่อยู่ที่การใช้งานของแต่ละโปรแกรมของจุดเข้าไบนารีอยู่ (เช่น_start()สัญลักษณ์บน Linux) ในตัวอย่างนั้นไฟล์ที่เรียกทำงานได้จะเป็นอ็อบเจกต์ (มันมี "ตารางการจัดส่ง" ของตัวเองและทุกอย่าง) และจุดเริ่มต้นคือการดำเนินการส่งแบบไดนามิก ดังนั้นจุดเข้าใช้งานของโปรแกรมจึงเสมือนจริงเมื่อดูจากภายนอกและสามารถทำให้ดูเสมือนจริงได้อย่างง่ายดายเมื่อดูจากภายในเช่นกัน
Theodoros Chatzigiannakis

1
"สิ่งที่จะทำให้แตกเพราะคุณมีวัตถุไร้สัญชาติกำลังบินอยู่" ++++++
RubberDuck

@TheodorosChatzigiannakis คุณสร้างข้อโต้แย้งที่แข็งแกร่ง หากไม่มีสิ่งอื่นใดที่คุณทำให้ฉันเชื่อมั่นว่าบรรทัดนั้นไม่ได้สร้างแรงบันดาลใจให้ฉัน ฉันพยายามอนุญาตให้ผู้คนยักยอกแนวทางปฏิบัติที่เป็นนิสัยบางอย่างที่พวกเขาเชื่อมโยงกับวิธีการคงที่ ดังนั้นฉันจึงอัพเดท คิด?
candied_orange

2

ทำไมวิธีการแบบคงที่ไม่สามารถเขียนทับได้?

มันไม่ใช่คำถามของ "ควร"

"การแทนที่" หมายถึง "การจัดส่งแบบไดนามิก " "วิธีการคงที่" หมายถึง "ส่งแบบคงที่" หากบางสิ่งบางอย่างคงที่จะไม่สามารถเขียนทับได้ หากสิ่งที่สามารถแทนที่มันไม่ได้คงที่

คำถามของคุณค่อนข้างคล้ายกับการถามว่า: "ทำไมสามล้อถึงไม่สามารถมีสี่ล้อได้?" คำนิยามของ "สามล้อ" คือการที่มีสามล้อ หากเป็นรถสามล้อก็ไม่สามารถมีสี่ล้อได้ถ้ามีสี่ล้อก็ไม่สามารถเป็นรถสามล้อได้ ในทำนองเดียวกันคำจำกัดความของ "วิธีการคงที่" ก็คือว่ามันถูกส่งแบบคงที่ หากเป็นวิธีคงที่จะไม่สามารถส่งแบบไดนามิกได้หากสามารถส่งแบบไดนามิกก็ไม่สามารถเป็นวิธีคงที่ได้

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


1
"ตามคำจำกัดความ" เอาล่ะ
candied_orange

1

ดังนั้นฟังก์ชั่นคงที่ใน C # ไม่สามารถเสมือนหรือนามธรรม

ใน C #, คุณมักจะเรียกสมาชิกคงใช้คลาสเช่นไม่BaseClass.StaticMethod() baseObject.StaticMethod()ดังนั้นในที่สุดหากคุณมีChildClassการสืบทอดจากBaseClassและchildObjectเป็นตัวอย่างของคุณจะไม่สามารถที่จะเรียกวิธีการแบบคงที่ของคุณจากChildClass childObjectคุณจะต้องใช้คลาสจริงเสมออย่างชัดเจนดังนั้นการstatic virtualไม่เข้าใจ

สิ่งที่คุณสามารถทำได้คือกำหนดstaticวิธีการเดียวกันในคลาสลูกของคุณและใช้newคำหลัก

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

หากเป็นไปได้ที่จะเรียกbaseObject.StaticMethod()คำถามของคุณจะสมเหตุสมผล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.