ทำไมต้องหลีกเลี่ยงการหล่อ? [ปิด]


97

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

แต่ถ้ามีคนขอให้ฉันอธิบายว่าทำไมถึงเป็นเช่นนั้นฉันก็คงมองพวกเขาเหมือนกวางในไฟหน้า

แล้วทำไม / เมื่อไหร่หล่อไม่ดี?

เป็นเรื่องทั่วไปสำหรับ java, c #, c ++ หรือทุกสภาพแวดล้อมรันไทม์ที่แตกต่างกันจัดการกับเงื่อนไขของมันเองหรือไม่?

ยินดีต้อนรับเฉพาะสำหรับภาษาใด ๆ เช่นทำไมภาษา c ++ จึงไม่ดี


3
คุณได้รับความประทับใจนี้มาจากไหน?
Oded

8
ฉันประทับใจตั้งแต่ฉันไม่เคยอ่านหนังสือหรือเจอโปรแกรมเมอร์ที่พูดว่า "แคสติ้งซูกู๊ด !!!"
LoudNPossablyWrong

15
คำตอบสำหรับ C ++ นั้นแตกต่างจากคำตอบสำหรับ C # อย่างหลีกเลี่ยงไม่ได้ นี่เป็นภาษาที่เฉพาะเจาะจงมาก คำตอบนี้ตอบคำถามนี้สำหรับภาษาเฉพาะและในบางกรณีไม่ได้ระบุว่าพวกเขากำลังพูดถึงภาษาอะไร
James McNellis

2
ไม่ดีเป็นคำที่สัมพันธ์กัน การหลีกเลี่ยงการแคสต์เป็นแนวทางปฏิบัติที่ดีที่สุด แต่บางครั้งโปรแกรมเมอร์ก็ต้องทำในสิ่งที่โปรแกรมเมอร์ต้องทำ (โดยเฉพาะอย่างยิ่งถ้าคุณเขียนโปรแกรม java 1.5+ ที่ใช้ไลบรารีที่เขียนขึ้นสำหรับ 1.4) อาจจะเปลี่ยนชื่อคำถามว่า "ทำไมจึงควรหลีกเลี่ยงการแคสต์"
Mike Miller

2
นี่เป็นคำถามที่ดีมาก .... ถูกปิดโดยผู้ใช้ 5 คนซึ่งแต่ละคนมีชื่อเสียงน้อยกว่า 10,000 คน !! การใช้อำนาจใหม่อย่างมากเกินไปอย่างเห็นได้ชัด ได้โหวตให้เปิดใหม่.
reach4thelasers

คำตอบ:


141

คุณได้ติดแท็กสิ่งนี้ด้วยสามภาษาและคำตอบนั้นแตกต่างกันมากระหว่างสามภาษา การสนทนาเกี่ยวกับ C ++ มากหรือน้อยแสดงถึงการอภิปรายเกี่ยวกับ C casts เช่นกันและนั่นก็ให้คำตอบที่สี่ (ไม่มากก็น้อย)

เนื่องจากเป็นสิ่งที่คุณไม่ได้กล่าวถึงอย่างชัดเจนฉันจะเริ่มด้วยการร่าย C. C มีปัญหาหลายประการ หนึ่งคือพวกเขาสามารถทำสิ่งต่างๆมากมายได้ ในบางกรณีนักแสดงไม่ได้ทำอะไรมากไปกว่าการบอกคอมไพเลอร์ (ในสาระสำคัญ): "หุบปากฉันรู้ว่าฉันกำลังทำอะไรอยู่" นั่นคือทำให้มั่นใจได้ว่าแม้ว่าคุณจะทำการแปลงที่อาจทำให้เกิดปัญหาคอมไพเลอร์ จะไม่เตือนคุณเกี่ยวกับปัญหาที่อาจเกิดขึ้นเหล่านั้น ตัวอย่างเช่นchar a=(char)123456;. ผลลัพธ์ที่แน่นอนของการใช้งานนี้กำหนดไว้ (ขึ้นอยู่กับขนาดและการลงนามของchar) และยกเว้นในสถานการณ์ที่ค่อนข้างแปลกอาจไม่มีประโยชน์ C casts ยังแตกต่างกันไปไม่ว่าจะเป็นสิ่งที่เกิดขึ้นในเวลาคอมไพล์เท่านั้น (กล่าวคือคุณแค่บอกคอมไพเลอร์ว่าจะตีความ / ปฏิบัติต่อข้อมูลบางอย่างอย่างไร) หรือสิ่งที่เกิดขึ้นในขณะทำงาน (เช่นการแปลงจริงจากสองครั้งเป็น ยาว).

C ++ พยายามจัดการกับสิ่งนั้นอย่างน้อยระดับหนึ่งโดยการเพิ่มตัวดำเนินการแคสต์ "ใหม่" จำนวนหนึ่งซึ่งแต่ละตัวถูก จำกัด ไว้เฉพาะความสามารถบางส่วนของ C cast นี้จะทำให้มันยากมากที่จะ (ตัวอย่าง) ตั้งใจทำแปลงที่คุณจริงๆไม่ได้ตั้งใจ - ถ้าคุณเพียงตั้งใจจะโยนไป constness บนวัตถุที่คุณสามารถใช้const_castและให้แน่ใจว่าเพียง แต่สิ่งที่มันสามารถส่งผลกระทบไม่ว่าจะเป็น วัตถุconst, volatileหรือไม่ ในทางกลับกัน a static_castไม่ได้รับอนุญาตให้ส่งผลกระทบต่อวัตถุconstหรือvolatile. ในระยะสั้นคุณมีความสามารถประเภทเดียวกันเกือบทั้งหมด แต่ถูกจัดหมวดหมู่เพื่อให้โดยทั่วไปแล้วนักแสดงคนหนึ่งสามารถทำการแปลงได้เพียงประเภทเดียวโดยที่นักแสดงรูปแบบ C ตัวเดียวสามารถทำการแปลงได้สองหรือสามครั้งในการดำเนินการเดียว ข้อยกเว้นหลักคือคุณสามารถใช้dynamic_castแทน a ได้static_castในบางกรณีและแม้จะเขียนเป็น a dynamic_castแต่มันก็จะกลายเป็นไฟล์static_cast. ตัวอย่างเช่นคุณสามารถใช้dynamic_castเพื่อเลื่อนขึ้นหรือลงตามลำดับชั้น แต่การโยน "ขึ้น" ลำดับชั้นนั้นปลอดภัยเสมอดังนั้นจึงสามารถทำได้แบบคงที่ในขณะที่การเหวี่ยง "ลง" ลำดับชั้นไม่จำเป็นต้องปลอดภัยดังนั้น ทำแบบไดนามิก

Java และ C # มีความคล้ายคลึงกันมากขึ้น โดยเฉพาะอย่างยิ่งเมื่อทั้งคู่แคสติ้งคือการรันไทม์เสมอ ในแง่ของตัวดำเนินการแคสต์ C ++ มักจะใกล้เคียงที่สุดกับdynamic_castสิ่งที่ทำได้จริง ๆ นั่นคือเมื่อคุณพยายามส่งวัตถุไปยังประเภทเป้าหมายบางประเภทคอมไพเลอร์จะแทรกการตรวจสอบรันไทม์เพื่อดูว่าการแปลงนั้นได้รับอนุญาตหรือไม่ และโยนข้อยกเว้นหากไม่ใช่ รายละเอียดที่แน่นอน (เช่นชื่อที่ใช้สำหรับข้อยกเว้น "bad cast") แตกต่างกันไป แต่หลักการพื้นฐานส่วนใหญ่ยังคงคล้ายกัน (แม้ว่าหน่วยความจำจะให้บริการ Java จะทำให้ casts ที่ใช้กับประเภทที่ไม่ใช่วัตถุบางประเภทเช่นintใกล้กับ C การร่าย - แต่ประเภทเหล่านี้ใช้น้อยพอที่ 1) ฉันจำไม่ได้แน่นอนและ 2) แม้ว่าจะเป็นเรื่องจริง แต่ก็ไม่สำคัญมากนัก)

เมื่อมองไปที่สิ่งต่างๆโดยทั่วไปแล้วสถานการณ์ค่อนข้างเรียบง่าย (อย่างน้อย IMO): นักแสดง (ชัดเจนเพียงพอ) หมายความว่าคุณกำลังแปลงบางสิ่งจากประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง เมื่อ / ถ้าคุณทำเช่นนั้นจะทำให้เกิดคำถามว่า "ทำไม?" หากคุณต้องการให้บางสิ่งเป็นประเภทเฉพาะจริงๆทำไมคุณไม่กำหนดให้เป็นประเภทนั้นเพื่อเริ่มต้นด้วย? ไม่ได้หมายความว่าไม่มีเหตุผลที่จะทำการแปลงเช่นนี้ แต่เมื่อใดก็ตามที่เกิดขึ้นควรถามคำถามว่าคุณสามารถออกแบบโค้ดใหม่ได้หรือไม่เพื่อให้ใช้ประเภทที่ถูกต้องตลอด แม้แต่การแปลงที่ดูเหมือนไม่มีพิษภัย (เช่นระหว่างจำนวนเต็มและทศนิยม) ก็ควรได้รับการตรวจสอบอย่างใกล้ชิดมากกว่าปกติ แม้จะดูเหมือนความคล้ายคลึงกันควรใช้จำนวนเต็มสำหรับประเภทของสิ่งที่ "นับ" และจุดลอยตัวสำหรับสิ่งที่ "วัดได้" การเพิกเฉยต่อความแตกต่างคือสิ่งที่นำไปสู่ข้อความบ้าๆเช่น "ครอบครัวอเมริกันโดยเฉลี่ยมีลูก 1.8 คน" แม้ว่าเราทุกคนจะเห็นว่ามันเกิดขึ้นได้อย่างไร แต่ความจริงก็คือไม่มีครอบครัวใดที่มีลูก 1.8 คน อาจมี 1 หรืออาจ 2 หรืออาจมีมากกว่านั้น - แต่ไม่เคย 1.8


10
ดูเหมือนว่าคุณกำลังทุกข์ทรมานกับ 'ความตายโดยช่วงความสนใจ' นี่เป็นคำตอบที่ดี
Steve Townsend

2
คำตอบที่ชัดเจน แต่บางส่วนของ "ฉันไม่รู้" อาจกระชับขึ้นเพื่อให้ครอบคลุม @ Dragontamer5788 เป็นคำตอบที่ดี แต่ไม่ครอบคลุม
M2tM

5
เด็กทั้งคนและเด็กที่ขาขาด 1 คนจะเป็นเด็ก 1.8 คน แต่คำตอบที่ดีมากไม่มีเลยแม้แต่น้อย :)
ออนซ์

1
คำตอบที่ดีมากไม่ได้ขัดขวางแม้แต่น้อยโดยการยกเว้นคู่รักที่ไม่มีลูก

@Roger: ไม่รวมไม่สนใจ
Jerry Coffin

48

คำตอบดีๆมากมายที่นี่ นี่คือวิธีที่ฉันมอง (จากมุมมอง C #)

การหล่อมักหมายถึงหนึ่งในสองสิ่ง:

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

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

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

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

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

ความคิดเพิ่มเติมบางประการที่นี่:

http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/


2
ฉันไม่คิดว่านั่นคือธงสีแดงโดยเนื้อแท้ หากคุณมีFooวัตถุที่สืบทอดมาจากBarและคุณเก็บไว้ใน a List<Bar>คุณจะต้องมีการร่ายหากคุณต้องการสิ่งนั้นFooกลับคืนมา บางทีอาจบ่งบอกถึงปัญหาในระดับสถาปัตยกรรม (ทำไมเราถึงเก็บBars แทนที่จะFooเป็น s?) แต่ไม่จำเป็น และหากFooมีการแคสต์ที่ถูกต้องintด้วยก็จะเกี่ยวข้องกับความคิดเห็นอื่น ๆ ของคุณเช่นกันคุณกำลังจัดเก็บ a Fooไม่ใช่intเพราะ an intไม่เหมาะสมเสมอไป
Mike Caron

6
@ Mike Caron - ฉันไม่สามารถตอบสำหรับ Eric ได้อย่างชัดเจน แต่สำหรับฉันแล้วธงสีแดงหมายถึง "นี่เป็นสิ่งที่ต้องคิด" ไม่ใช่ "นี่เป็นสิ่งที่ผิด" และไม่มีปัญหาในการจัดเก็บFooใน a List<Bar>แต่ ณ จุดที่นักแสดงคุณกำลังพยายามทำบางสิ่งบางอย่างFooที่ไม่เหมาะสมกับไฟล์Bar. นั่นหมายความว่าพฤติกรรมที่แตกต่างกันสำหรับชนิดย่อยจะถูกทำผ่านกลไกอื่นที่ไม่ใช่ความหลากหลายในตัวที่จัดเตรียมโดยวิธีการเสมือน บางทีนั่นอาจเป็นวิธีแก้ปัญหาที่ถูกต้อง แต่มักจะเป็นธงสีแดง
Jeffrey L Whitledge

14
แน่นอน. ถ้าคุณดึงสิ่งของออกจากรายชื่อสัตว์และคุณต้องบอกคอมไพเลอร์ในภายหลังว่าฉันรู้ว่าตัวแรกคือเสือตัวที่สองคือสิงโตและตัวที่สาม เป็นหมีคุณควรใช้ทูเพิล <สิงโตเสือหมี> ไม่ใช่รายการ <สัตว์>
Eric Lippert

5
ฉันเห็นด้วยกับคุณ แต่ฉันคิดว่าหากไม่มีการรองรับวากยสัมพันธ์ที่ดีกว่าสำหรับสิ่งTuple<X,Y,...>ทูเปิลไม่น่าจะมีการใช้ C # อย่างแพร่หลาย นี่เป็นสถานที่หนึ่งที่ภาษาสามารถช่วยผลักดันผู้คนไปสู่ ​​"หลุมแห่งความสำเร็จ" ได้ดีขึ้น
kvb

4
@kvb: ฉันเห็นด้วย เราพิจารณาการนำไวยากรณ์ทูเปิลมาใช้สำหรับ C # 4 แต่ไม่เหมาะสมกับงบประมาณ บางทีใน C # 5; เรายังไม่ได้กำหนดคุณลักษณะทั้งหมด ยุ่งเกินไปในการรวบรวม CTP สำหรับ async หรืออาจจะเป็นรุ่นอนาคตที่สมมุติขึ้น
Eric Lippert

36

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

อย่างที่บอกไว้ข้างบน. นี่ไม่ได้หมายความว่าการหล่อทั้งหมดไม่ดี แต่ถ้าสามารถหลีกเลี่ยงได้ก็ควรทำเช่นนั้น


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

5
อันที่จริงการแบนนี้ผิดใน C ++ บางทีการแก้ไขเพื่อรวมภาษาที่กำหนดเป้าหมายด้วยข้อมูลนี้อาจเป็นไปตามลำดับ
Steve Townsend

@Erick - ฉันรู้ แต่ฉันไม่รู้สิ่งแรกเกี่ยวกับ Java หรือรายละเอียด C # เพียงพอเพื่อให้แน่ใจว่าข้อมูลนั้นถูกต้อง
Steve Townsend

ขอโทษด้วยฉันเป็นคน Java ดังนั้นความรู้ c ++ ของฉันจึงมี จำกัด เพียงพอ ฉันสามารถแก้ไขคำว่า "เทมเพลต" ได้เนื่องจากตั้งใจให้คลุมเครือ
Mike Miller

ไม่เป็นเช่นนั้น ข้อผิดพลาดในการแคสต์บางอย่างถูกจับโดยคอมไพเลอร์ Java ไม่ใช่แค่ข้อผิดพลาดทั่วไปเช่นกัน พิจารณา (สตริง) 0;
Marquis of Lorne

18

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

หากเป็นเรื่องเลวร้ายในระดับสากลภาษาจะไม่รองรับ เช่นเดียวกับคุณลักษณะภาษาอื่น ๆ ก็มีที่มา

คำแนะนำของฉันคือมุ่งเน้นไปที่ภาษาหลักของคุณและทำความเข้าใจกับการแสดงทั้งหมดและแนวทางปฏิบัติที่ดีที่สุดที่เกี่ยวข้อง ที่ควรแจ้งการทัศนศึกษาเป็นภาษาอื่น ๆ

เอกสารที่เกี่ยวข้อง C # อยู่ที่นี่

มีบทสรุปที่ดีใน c ++ ตัวเลือกเป็นที่เป็นคำถาม SO ก่อนหน้านี้ที่นี่


9

ฉันพูดถึงC ++เป็นส่วนใหญ่ที่นี่ แต่ส่วนใหญ่อาจใช้กับ Java และ C # ด้วย:

C ++ เป็นภาษาพิมพ์แบบคงที่ มีบางอย่างที่ภาษาอนุญาตให้คุณใช้ในสิ่งนี้ (ฟังก์ชันเสมือนการแปลงโดยนัย) แต่โดยพื้นฐานแล้วคอมไพเลอร์จะรู้ประเภทของทุกออบเจ็กต์ในเวลาคอมไพล์ เหตุผลที่จะใช้เช่นภาษาคือว่าข้อผิดพลาดสามารถจับที่รวบรวมเวลา ถ้าคอมไพลเลอร์รู้ประเภทของaและคอมไพเลอร์bจะจับคุณในเวลาคอมไพล์เมื่อคุณทำa=bโดยที่aจำนวนเชิงซ้อนและbเป็นสตริง

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


5

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

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


5
ความปลอดภัยประเภท "บายพาส" ไม่ได้ร่ายทั้งหมดใน C ++
James McNellis

4
ความปลอดภัยประเภท "บายพาส" ไม่ใช่ทั้งหมดใน C #

5
ความปลอดภัยประเภท "บายพาส" ไม่ได้แคสต์ทั้งหมดใน Java
Erick Robertson

11
ความปลอดภัยประเภท "บายพาส" ไม่ใช่ทั้งหมดในการแคสต์ เดี๋ยวก่อนแท็กนั้นไม่ได้หมายถึงภาษา ...
sbi

2
ในเกือบทุกกรณีใน C # และ Java การแคสต์จะทำให้คุณลดประสิทธิภาพเนื่องจากระบบจะทำการตรวจสอบประเภทรันไทม์ (ซึ่งไม่ฟรี) ใน C ++ dynamic_castโดยทั่วไปจะช้ากว่าstatic_castเนื่องจากโดยทั่วไปจะต้องทำการตรวจสอบประเภทรันไทม์ (มีข้อแม้บางประการ: การหล่อเป็นประเภทฐานมีราคาถูก ฯลฯ )
Travis Gockel

4

การหล่อบางประเภทมีความปลอดภัยและมีประสิทธิภาพมากจนมักไม่ได้รับการพิจารณาว่าหล่อเลยด้วยซ้ำ

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

หากคุณร่ายจากประเภทที่เรียบง่ายเช่น int ไปยังประเภทที่กว้างกว่าเช่น int ยาวอีกครั้งก็มักจะค่อนข้างถูก (โดยทั่วไปจะไม่แพงกว่าการกำหนดประเภทเดียวกันกับการร่ายให้) และอีกครั้งก็ปลอดภัย

ประเภทอื่น ๆ มีราคาแพงกว่าและ / หรือแพงกว่า ในภาษาส่วนใหญ่การแคสต์จากประเภทพื้นฐานไปยังประเภทที่ได้รับนั้นมีราคาถูก แต่มีความเสี่ยงสูงที่จะเกิดข้อผิดพลาดอย่างรุนแรง (ใน C ++ ถ้าคุณ static_cast จากฐานถึงที่มาจะมีราคาถูก แต่ถ้าค่าพื้นฐานไม่ใช่ประเภทที่ได้รับ พฤติกรรมไม่ได้กำหนดและอาจแปลกมาก) หรือค่อนข้างแพงและมีความเสี่ยงที่จะเพิ่มข้อยกเว้น (dynamic_cast ใน C ++, แคสต์พื้นฐานที่ได้มาอย่างชัดเจนใน C # และอื่น ๆ ) การชกมวยใน Java และ C # เป็นอีกตัวอย่างหนึ่งของสิ่งนี้และมีค่าใช้จ่ายที่มากขึ้น (เนื่องจากมีการเปลี่ยนแปลงมากกว่าการปฏิบัติตามค่าพื้นฐาน)

การแคสต์ประเภทอื่นอาจสูญเสียข้อมูล (ประเภทจำนวนเต็มแบบยาวไปยังประเภทจำนวนเต็มสั้น ๆ )

กรณีของความเสี่ยงเหล่านี้ (ไม่ว่าจะเป็นข้อยกเว้นหรือข้อผิดพลาดที่ร้ายแรงกว่า) และค่าใช้จ่ายล้วนเป็นเหตุผลที่จะหลีกเลี่ยงการคัดเลือกนักแสดง

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

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


3

มีแนวโน้มเพิ่มมากขึ้นสำหรับโปรแกรมเมอร์ที่จะยึดมั่นในกฎเกณฑ์เกี่ยวกับการใช้คุณลักษณะของภาษา ("ไม่เคยใช้ XXX!", "XXX ถือว่าเป็นอันตราย" ฯลฯ ) โดย XXX จะมีตั้งแต่gotoตัวชี้ไปจนถึงตัวชี้ไปจนถึงprotectedสมาชิกข้อมูล มูลค่า.

การปฏิบัติตามกฎดังกล่าวจากประสบการณ์ของฉันทำให้มั่นใจได้สองประการ: คุณจะไม่เป็นโปรแกรมเมอร์ที่แย่และจะไม่เป็นโปรแกรมเมอร์ที่ยอดเยี่ยม

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

"โดยทั่วไปฉันจะหลีกเลี่ยงการแคสต์ประเภทให้มากที่สุด" เป็นตัวอย่างที่ดีของกฎที่มีการกำหนดค่ามากเกินไป การร่ายมีความจำเป็นในสถานการณ์ทั่วไปหลายประการ ตัวอย่างบางส่วน:

  1. เมื่อทำงานร่วมกับรหัสของบุคคลที่สาม (โดยเฉพาะอย่างยิ่งเมื่อรหัสนั้นเต็มไปด้วยtypedefs) (ตัวอย่าง: GLfloat<--> double<--> Real.)
  2. การแคสต์จากตัวชี้ / การอ้างอิงคลาสฐานที่ได้รับมา: นี่เป็นเรื่องธรรมดาและเป็นธรรมชาติที่คอมไพเลอร์จะทำโดยปริยาย หากทำให้ชัดเจนจะเพิ่มความสามารถในการอ่านนักแสดงคือก้าวไปข้างหน้าไม่ใช่ถอยหลัง!
  3. การส่งจากฐานไปยังตัวชี้ / การอ้างอิงคลาสที่ได้รับ: เป็นเรื่องธรรมดาเช่นกันแม้ในโค้ดที่ออกแบบมาอย่างดี (ตัวอย่าง: ภาชนะที่แตกต่างกัน)
  4. ภายในการทำให้เป็นอนุกรมไบนารี / deserialization หรือรหัสระดับต่ำอื่น ๆ ที่ต้องการเข้าถึงไบต์ดิบของชนิดในตัว
  5. เมื่อใดก็ตามที่มันเป็นธรรมชาติมากขึ้นสะดวกและอ่านได้ที่จะใช้ประเภทอื่น (ตัวอย่าง: std::size_type-> int.)

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


1

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

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


1

ในกรณีของ C # ต้องใช้ความระมัดระวังมากขึ้นในการแคสต์เนื่องจากมีค่าใช้จ่ายในการชกมวย / การแกะกล่องที่เกี่ยวข้องในขณะที่ต้องจัดการกับประเภทมูลค่า


1

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


1

คุณโยนวัตถุเป็นบางประเภทเท่านั้นหากตรงตามเงื่อนไข 2 ประการ:

  1. คุณรู้ว่ามันเป็นประเภทนั้น
  2. คอมไพเลอร์ไม่ได้

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

ตอนนี้เมื่อคุณทำการแคสอาจมี 2 เหตุผลที่แตกต่างกัน:

  1. คุณทำได้ไม่ดีในการแสดงความสัมพันธ์ประเภท
  2. ระบบประเภทภาษาไม่สามารถแสดงออกได้เพียงพอที่จะใช้เป็นวลี

ในภาษาส่วนใหญ่คุณจะพบกับสถานการณ์ที่ 2 หลายครั้ง Generics เช่นเดียวกับใน Java ช่วยได้เล็กน้อยระบบเทมเพลต C ++ ยิ่งขึ้น แต่ก็ยากที่จะเชี่ยวชาญและถึงแม้บางอย่างอาจเป็นไปไม่ได้หรือไม่คุ้มกับความพยายาม

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


0

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

ไชโย!


0

เพื่อให้กระชับจริงๆเหตุผลที่ดีเป็นเพราะการพกพา สถาปัตยกรรมที่แตกต่างกันที่ทั้งสองรองรับภาษาเดียวกันอาจมี ints ขนาดต่างกัน ดังนั้นถ้าฉันย้ายจาก ArchA ไปยัง ArchB ซึ่งมี int ที่แคบกว่าฉันอาจเห็นพฤติกรรมแปลก ๆ ที่ดีที่สุดและการทำ seg ก็แย่ที่สุด

(ฉันละเลยสถาปัตยกรรมที่เป็นอิสระจาก bytecode และ IL อย่างชัดเจน)

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