กับดักอินเทอร์เฟซของ Delphi
ตอนนี้ฉันรู้ข้อผิดพลาดที่สำคัญสองประการ:
กับดัก 1. กับดักการแปลงประเภทอินเตอร์เฟส
A) คุณไม่สามารถใช้การอ้างอิงวัตถุไปยังอินเทอร์เฟซที่ไม่ได้ประกาศประเภทของการอ้างอิงนี้แม้ว่าวัตถุจะใช้อินเทอร์เฟซนี้จริง (ฮิฮิข้อดีนั้นยากที่จะออกเสียง) b) เมื่อตัวแปรวัตถุถูกกำหนดให้กับตัวแปรอินเตอร์เฟสและเมื่อตัวแปรอินเตอร์เฟสถูกกำหนดให้กับตัวแปรวัตถุที่อยู่ของตัวแปรวัตถุมีการเปลี่ยนแปลงนั่นคือมันไม่ได้เป็นวัตถุดั้งเดิมอีกต่อไป แต่ชี้ไปที่ที่อยู่ข้อผิดพลาด . ตัวอย่างเช่น: i1 = อินเทอร์เฟซ
ฟังก์ชั่นทำ: บูลีน;
จบ;
tc1 = คลาส
Att1: จำนวนเต็ม;
จบ;
TC2 = คลาส (TC1, I1)
Att2: จำนวนเต็ม;
ฟังก์ชั่นทำ: บูลีน;
สิ้นสุด; intf1: i1; obj1: tc!; obj2: tc2; obj2: = tc2.create;
obj1: = obj2
i1 (obj2) .do;
i1 (obj1) .do; เนื่องจากประเภท TC1 ของ OBJ1 ไม่ได้ประกาศว่า I1 ถูกนำไปใช้จึงไม่สามารถแปลงเป็น I1 ได้แม้ว่า OBJ1 จะใช้ I1 นอกจากนี้หากคุณแปลงวัตถุเป็นอินเทอร์เฟซแล้วกลับมาก็จะมีปัญหา obj2: = tc2.create; obj2.att1: = 0;
intf1: = obj2; // ถูกต้อง OBJ2: = INTF1; OBJ2.ATT1: = 0; นั่นคือหลังจากแปลงจากการอ้างอิงวัตถุเป็นการอ้างอิงตัวชี้ที่อยู่จะเปลี่ยนไป แต่เมื่อการอ้างอิงตัวชี้ถูกหันกลับจากการอ้างอิงวัตถุแล้วกลับไปที่การอ้างอิงวัตถุ (ข้อผิดพลาดของ Delphi?)
กับดัก 2. การจัดการอายุการใช้งานอินเตอร์เฟส
ขึ้นอยู่กับสามัญสำนึกของฉัน (นี่คือสามัญสำนึกการเขียนโปรแกรมไม่ใช่สามัญสำนึกการใช้งาน Delphi) ฉันคิดว่าอินเทอร์เฟซไม่จำเป็นต้องมีการจัดการอายุการใช้งานเพราะอินเทอร์เฟซไม่สามารถสร้างวัตถุจริงได้เลย แต่ Delphi ตีสามัญสำนึกของฉันอีกครั้ง (ฮะทำไมคุณถึงพูดว่า "ต่ออายุ"?) อินเทอร์เฟซของมันมีอายุการใช้งานและต้องใช้สามวิธีต่อไปนี้: ฟังก์ชั่น queryinterface (const iid: tguid; out obj): hresult; stdcall; ฟังก์ชั่น _addref: จำนวนเต็ม; ดังนั้นฉันไม่รู้ว่าจะใช้วิธีการทั้งสามนี้ได้อย่างไร j หากคุณไม่ต้องการใช้วิธีการทั้งสามนี้ด้วยตัวคุณเองคุณสามารถใช้ TComponent ได้ เนื่องจาก TComponent ได้ใช้วิธีการทั้งสามนี้จึงสามารถสืบทอดได้ดังนั้นจึงไม่จำเป็นต้องใช้วิธีการทั้งสามนี้ คุณสามารถใช้มันด้วยความมั่นใจได้หรือไม่? คำตอบคือไม่ เพราะ Delphi อย่างลับๆ (เพราะมันเกินความคาดหมายของฉัน) เมื่อคุณตั้งค่าตัวแปรอินเตอร์เฟสเป็น NIL ฟังก์ชั่น _intfclear (var dest: iinterface): pointer; var p: pointer; ) ._release; ฟังก์ชั่น tComponent._release: จำนวนเต็ม; แล้วไม่มีอะไรจะไม่ทำ สิ่งที่เราไม่ได้ทำงานเป็นวัตถุ COM ดังนั้นจึงไม่มีปัญหา? คำตอบยังคงเป็นเช่นนั้นพิจารณาสถานการณ์ต่อไปนี้: = tc2.create; tryintf1: = obj2; intf1.do; ข้อผิดพลาดในการเข้าถึงที่อยู่ผิดกฎหมายจะเกิดขึ้น ทำไม ดังที่ได้กล่าวไว้ข้างต้นเมื่อการอ้างอิงอินเทอร์เฟซถูกตั้งค่าเป็น NIL _intfClear จะถูกเรียกและ _intfclear จะเรียกว่า _release ของวัตถุในเวลานี้วัตถุได้รับการปล่อยตัวและจะมีข้อผิดพลาดในการเข้าถึงที่อยู่ผิดกฎหมาย บางคนบอกว่ามันไม่จำเป็น? obj2: = tc2.create; tryintf1: = obj2; intf1.do; ทำไม เนื่องจากคอมไพเลอร์ Delphi นั้นฉลาดจึงคิดว่าคุณลืมที่จะตั้งค่าการอ้างอิงที่อยู่นี้ให้เป็น NIL ดังนั้นคุณจะเพิ่มมันให้คุณโดยอัตโนมัติ วิธีแก้ปัญหา? วิธีที่ 1: ตั้งค่าการอ้างอิงอินเตอร์เฟสเป็น NIL ก่อนจากนั้นปล่อยวัตถุ intf1: = nil; obj2.free; Pointer (Intf1): = NIL; ฉันมักจะใช้วิธีที่สองดังนั้นคุณไม่ต้องคิดว่าจะปล่อยใครก่อน ยิ่งไปกว่านั้นในรูปแบบการออกแบบบางอย่างคุณอาจมีการอ้างอิงอินเทอร์เฟซเท่านั้นและคุณไม่ทราบว่าจะใช้วัตถุที่อ้างอิงเมื่อใด ตัวอย่างเช่นพิจารณารูปแบบคอมโพสิต tComposite = คลาส (tComponent, i1) interlist ส่วนตัว: txContainer; // คลาสคอนเทนเนอร์ที่เก็บข้อมูลอ้างอิงอินเตอร์เฟสสำหรับ "Leaf" ขั้นตอนสาธารณะเพิ่ม (AINTF: I1); เห็นได้ชัดว่าไม่เช่นนั้น "ใบ" จะถูกปล่อยออกมาช้ากว่าวัตถุ "วัตถุสังเคราะห์" นี้หรือไม่? ฉันไม่คิดอย่างนั้น หากกฎระเบียบนี้ได้รับคำสั่งความยืดหยุ่นจะหายไปมาก ดังนั้นเราจึงคิดว่าเมื่อมีการอ้างอิงอินเทอร์เฟซเหล่านี้ไม่มีความสัมพันธ์จะไม่มีความสัมพันธ์กับวัตถุต้นฉบับเพื่อหลีกเลี่ยงข้อผิดพลาดในการเข้าถึงที่อยู่ที่ผิดกฎหมายหลังจากที่วัตถุถูกปล่อยออกมา คุณควรพิจารณาใช้คอนเทนเนอร์อะไร อาร์เรย์? tlist? tinterfacelist? ก่อนอื่นฉันคิดว่ามันต้องเป็น tinterfacelist เพราะสิ่งที่เราต้องการรองรับคืออินเทอร์เฟซ แต่เมื่อฟรีเขามันจะทำให้อินเทอร์เฟซทั้งหมดรองรับ NIL ซึ่งเป็นสิ่งที่เราไม่ต้องการ หรือเราสามารถแปลงการอ้างอิงอินเทอร์เฟซที่เก็บไว้ในตัวชี้ก่อนที่จะฟรีจากนั้นตั้งค่าเป็นศูนย์ สำหรับ i: = 0 ถึง interlist.count -1 dopointer (interlist.items [i]): = nil; จากนั้นเราลองอาร์เรย์ Interlist: อาร์เรย์ของ I1; อาร์เรย์แบบไดนามิกไม่ควรปล่อยออกมา ดูเหมือนว่าจะมีประโยชน์มาก แต่เมื่อคอมไพเลอร์ปล่อยออกมามันจะยังคงตั้งค่าแต่ละองค์ประกอบเป็นศูนย์และเป็นอินเทอร์เฟซยังมีความเป็นไปได้ที่จะมีข้อผิดพลาดในการเข้าถึงที่อยู่ผิดกฎหมาย สามารถทำได้ด้วยวิธีนี้สำหรับ i: = ต่ำ (arr) ถึงสูง (arr) dopointer (arr [i]): = nil; เวลาที่คุณใช้และคุณอาจจำไม่ได้ว่าจะทำหรือไม่ ในที่สุดใช้ tlist อย่างไรก็ตามมีตัวชี้ใน tlist ดังนั้นเมื่อเพิ่มคุณต้องดำเนินการดังนี้: xxx.add (aintf: i1) เริ่มต้น interlist.add (ตัวชี้ (aintf)); ต้องการการประมวลผลพิเศษเมื่อปล่อย ดูเหมือนว่าจะสมบูรณ์แบบ แต่ก็ยังมีกับดัก interlist.add (tc2.create); หรือ obj2: = tc2.create; interlist.add (obj2); เนื่องจากเป็นตัวชี้บริสุทธิ์เมื่อแปลงเป็นอินเทอร์เฟซการแปลงที่อยู่ของวัตถุที่อ้างอิงถึงการอ้างอิงอินเทอร์เฟซไม่ได้ดำเนินการ (ไม่ทราบวิธีการทำ) ดังนั้นเมื่อเรียกวิธีการที่ประกาศโดยอินเทอร์เฟซ เป็นข้อผิดพลาดในการเข้าถึงที่อยู่ที่ผิดกฎหมายอีกข้อหนึ่ง นี่เป็นวิธีเดียวที่จะเขียน: interlist.add (ตัวชี้ (i1 (tc2.create)) แม้ว่ามันจะลำบากเล็กน้อยนี่เป็นทางออกที่ดีที่สุด (อย่างที่ฉันรู้) เพราะถ้าคุณลืมว่าการถ่ายโอนทำให้เกิดข้อผิดพลาดในการโทรครั้งแรกมันจะง่ายกว่าที่จะค้นหาข้อผิดพลาด (เมื่อเทียบกับการใช้อาร์เรย์) ดังนั้นฉันขอแนะนำ: 1. ใช้ tlist เพื่อจัดการการอ้างอิงอินเทอร์เฟซ 2. เมื่อเพิ่มคุณควรแปลงวัตถุเป็นอินเทอร์เฟซแล้วเป็นตัวชี้ 3. สำหรับการอ้างอิงอินเทอร์เฟซที่ไม่ได้รับการจัดการโดยใช้ tlist สำหรับการอ้างอิงถึงอินเทอร์เฟซคุณต้องตั้งค่าเป็น NIL ด้วยตนเองโดยวิธีการต่อไปนี้: ตัวชี้ (intfref): = nil; ดังนั้นการสืบทอดมาจากมันยังสามารถช่วยตัวเองให้มีปัญหาในการใช้วิธีการทั้งสามนี้ แต่ _release ของมันถูกนำมาใช้เช่นนี้: ฟังก์ชั่น tinterfacedObject._release: จำนวนเต็ม; วัตถุที่ฉันตกใจในเวลานั้นขอบคุณที่ฉันไม่เคยใช้ นี่คือวิธีที่มันถูกนำไปใช้มันนำมาซึ่งปัญหามากกว่าการสืบทอดจาก TComponent ไม่แนะนำให้ใช้เป็นอย่างอื่น การอภิปรายทั้งหมดเกี่ยวกับอินเทอร์เฟซข้างต้นนั้น จำกัด อยู่ที่อินเทอร์เฟซระดับภาษาที่ใช้โดยทั่วไปและไม่มีการอภิปรายเกี่ยวกับอินเทอร์เฟซ COM+ การใช้งานอินเทอร์เฟซแปลก ๆ ของ Delphi มีส่วนเกี่ยวข้องกับการพัฒนาจากอินเทอร์เฟซ COM+ ซึ่งเป็นผลมาจากการประนีประนอมเนื่องจากภาระทางประวัติศาสตร์ที่หนักหน่วง