ต้นและปลายมีผลผูกพันคืออะไร?


77

ฉันได้ยินเรื่องเกี่ยวกับการมีส่วนร่วม แต่เนิ่นๆและช้า แต่ฉันไม่เข้าใจว่ามันคืออะไร ฉันพบคำอธิบายต่อไปนี้ซึ่งฉันไม่เข้าใจ:

การรวมก่อนหน้าหมายถึงการกำหนดค่าให้กับตัวแปรในช่วงเวลาการออกแบบในขณะที่การรวมภายหลังหมายถึงการกำหนดค่าให้กับตัวแปรในระหว่างเวลาทำงาน

มีคนช่วยอธิบายการผูกมัดทั้งสองประเภทแล้วเปรียบเทียบได้ไหม?


1
รวบรวมเวลากับรันไทม์
barlop

นี่คือการอ่านที่ดีเกี่ยวกับเรื่อง: en.wikibooks.org/wiki/Introduction_to_Programming_Languages/…
Jay Elston

คำตอบ:


84

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

ประเภท

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

นำมาใช้บ่อยครั้งโดยใช้ [ไดนามิก] ประเภทไดนามิก, วิปัสสนา / ไตร่ตรอง, แฟล็กและตัวเลือกคอมไพเลอร์หรือผ่านวิธีเสมือนโดยยืมและขยายการจัดส่งแบบไดนามิก

การเชื่อมก่อนกำหนด : ชนิดเป็นที่รู้จักกันก่อนที่จะมีการใช้งานตัวแปรในช่วงเวลาทำงานโดยปกติผ่านวิธีการคงที่การประกาศ

นำมาใช้บ่อยครั้งโดยใช้ชนิดดั้งเดิมดั้งเดิม

ฟังก์ชั่น

Static Dispatch : เป็นที่รู้จักกันเฉพาะฟังก์ชั่นหรือรูทีนย่อยที่รวบรวมเวลา; มันไม่ชัดเจนและจับคู่โดยลายเซ็น

ดำเนินการเป็นฟังก์ชั่นคงที่; ไม่มีวิธีใดที่สามารถมีลายเซ็นเดียวกันได้

Dynamic Dispatch : ไม่ใช่ฟังก์ชั่นหรือรูทีนย่อยเฉพาะในเวลารวบรวม กำหนดโดยบริบทระหว่างการดำเนินการ มีวิธีการที่แตกต่างกันสองวิธีในการ "จัดส่งแบบไดนามิก" แยกแยะโดยข้อมูลบริบทที่ใช้ในการเลือกการใช้งานฟังก์ชั่นที่เหมาะสม

ในการจัดส่งแบบไดนามิก [ เดียว ] เฉพาะประเภทของอินสแตนซ์ที่ใช้ในการพิจารณาการใช้งานฟังก์ชั่นที่เหมาะสม ในภาษาที่พิมพ์แบบสแตติกสิ่งนี้หมายถึงในทางปฏิบัติคือประเภทอินสแตนซ์ตัดสินใจว่าการใช้วิธีการใดจะใช้โดยไม่คำนึงถึงประเภทการอ้างอิงที่ระบุเมื่อมีการประกาศ / กำหนดตัวแปร เนื่องจากมีเพียงชนิดเดียว - ชนิดของอินสแตนซ์ของวัตถุ - ถูกใช้เพื่ออนุมานการใช้งานที่เหมาะสมวิธีการนี้เรียกว่า "การแจกจ่ายเดี่ยว"

นอกจากนี้ยังมีการจัดส่ง [ ไดนามิก ] หลายครั้งที่ประเภทพารามิเตอร์อินพุตยังช่วยพิจารณาว่าการใช้งานฟังก์ชันใดในการโทร เนื่องจากมีหลายประเภท - ทั้งประเภทของอินสแตนซ์และประเภทของพารามิเตอร์มีอิทธิพลต่อการเลือกใช้งานวิธีการวิธีการนี้จึงขนานนามว่า "ส่งหลายรายการ"

นำมาใช้เป็นฟังก์ชั่นเสมือนหรือนามธรรม; เบาะแสอื่น ๆ รวมถึงวิธีการแทนที่ซ่อนหรือเงา

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

ค่า

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

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

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

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

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

มีการใช้งานบ่อยครั้งเพื่อให้บริการที่สะอาดขึ้นการซิงโครไนซ์ที่สอดคล้องกันระหว่างแง่มุมการใช้งานที่แตกต่างกัน (เช่นมุมมองแบบจำลองเพื่อดูแบบจำลองไปยังตัวควบคุม ฯลฯ ) และพูดคุยเกี่ยวกับแนวความคิดเช่นต้นทางและปลายทาง on_bind, on_property_change, on_explicit, on_out_of_scope


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


2
คำตอบนี้ดูเหมือนเฉพาะเจาะจงกับภาษาเชิงวัตถุมากเกินไป
แจ็ค

27

อะไรที่จะตัดสินใจโดยคอมไพเลอร์ในขณะที่รวบรวมสามารถอ้างถึงEARLY / รวบรวม TIMEผูกพันและสิ่งที่จะตัดสินใจในรันไทม์เรียกว่าล่าช้า / รันไทม์ผูกพัน

ตัวอย่างเช่น,

วิธีการมากไปและวิธีการเอาชนะ

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

2) ในการแทนที่เมธอดมันจะตัดสินใจที่ RUNTIME ซึ่งจะเรียกเมธอดนั้น ดังนั้นจึงเป็น reffered ผูก

พยายามทำให้ง่ายและง่ายต่อการรับ หวังว่านี่จะช่วยได้


9

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

class A
{
public:
    void f() {}
    virtual void g() {}
};

class B : public A
{
    void f() {}
    virtual void g() {}
};

int main()
{
    A* a = new B;
    a->f();
    a->g();
}

ในตัวอย่างนี้a->f()จะเรียกจริง ๆvoid A::f()เพราะมันเป็นช่วงต้น (หรือคงที่) ผูกพันและดังนั้นโปรแกรมที่รันไทม์คิดว่ามันเป็นเพียงตัวชี้ไปยังAตัวแปรประเภทในขณะที่a->g()จริงจะเรียกvoid B::g()เพราะคอมไพเลอร์เห็นg()เป็นเสมือนฉีดรหัสเพื่อดู ที่อยู่ของฟังก์ชั่นที่ถูกต้องในการเรียกใช้งานจริง


1
"รันไทม์"? คุณกำลังพูดเกี่ยวกับ C ++ คอมไพล์ C ++ ตรงไปยังรหัสเครื่องมันไม่จำเป็นต้องมี runtime เพื่อแก้ไขเมธอดเสมือน
tdammers

3
@tdammers C ++ จริง ๆ แล้วต้องการไลบรารีรันไทม์ แต่ไม่ใช่สำหรับการโทรเสมือน หากคุณอ่านอย่างระมัดระวังคุณจะสังเกตเห็นว่าคำตอบนี้บอกคอมไพเลอร์ "injects รหัสเพื่อค้นหาที่อยู่ของฟังก์ชันที่ถูกต้อง [... ] at runtime"

ทว่า แต่ "รหัสเพื่อค้นหาที่อยู่ของฟังก์ชั่นที่ถูกต้อง" นั้นเป็นเพียงการชี้ไปที่ตัวชี้สองขั้นตอนแบบไม่เชื่อเรื่องพระเจ้าตามด้วยการเรียกฟังก์ชัน ไม่มี "ความคิด" ที่เกี่ยวข้อง เหตุผลเดียวที่มันทำงานได้อย่างน่าเชื่อถือเป็นเพราะคอมไพเลอร์ไม่ประเภทการตรวจสอบที่รวบรวมเวลา ; ณ รันไทม์, โค้ดที่สร้างไว้วางใจคอมไพเลอร์ให้ทำการตรวจสอบชนิด หากคุณใช้การส่งแบบไม่ปลอดภัย (เช่นการชี้แบบ C-style) คุณสามารถปฏิบัติต่อวัตถุ C ++ อย่างถูกกฎหมายในฐานะวัตถุของคลาสที่ไม่ถูกต้อง แต่ vtables จะถูกทำให้ยุ่งเหยิงจนสุดความสามารถ
tdammers

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

1
@tdammers และโดย "runtime" ฉันหมายถึง "โปรแกรมที่รันไทม์" เห็นได้ชัดว่า C ++ ไม่ได้รับการจัดการ แต่เมื่อคุณแสดงให้ฉันเห็นมันอาจทำให้เกิดความสับสนฉันเปลี่ยนมันเป็นถ้อยคำเต็ม
Yam Marcovic

5

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

  int add(int x,int y)
  {
    return x+y;
  }
  int sub(int x,int y)
  {
      return x-y;
  }


    int main()
    {
     //get user choice
     int(*fp)(int,int);
     //if add
      fp=add;
     //else if sub
     fp=sub;
     cout<<fp(2,2);
    }

ฟังก์ชั่นที่นี่เพิ่มและย่อยเป็นฟังก์ชั่น (ที่อยู่ของมันจะถูกผูกไว้ที่รวบรวมเวลา linker)

แต่ตัวชี้ฟังก์ชั่นมีผลผูกพันปลาย fp สามารถเรียกเพิ่มหรือย่อยขึ้นอยู่กับทางเลือกของผู้ใช้ [ที่รันไทม์]


3

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

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

ตัวอย่างที่แสดงให้เห็นถึงความผูกพันปลายในทับทิม:

a = 1 # a is an integer at this point
a.succ # asking for its successor is valid

class A
  def method_a
    # some code
  end
end

a = A.new
a.method_a # this is also valid
a.succ # this is not valid


class A # we can re-open the class and add a method
  def succ
    # some more code
  end
end
a.succ # now this is valid

ลำดับของการกระทำข้างต้นเป็นไปไม่ได้ในภาษาเช่น Java ที่ทุกประเภทได้รับการแก้ไขในเวลาทำงาน


1

แทนที่จะให้คำนิยามเชิงวิชาการแก่คุณฉันจะพยายามแสดงให้คุณเห็นความแตกต่างบางอย่างโดยใช้ตัวอย่างโลกแห่งความจริงโดยใช้ VBA:

การรวมก่อนหน้า:

Dim x As FileSystemObject
Set x = New FileSystemObject
Debug.Print x.GetSpecialFolder(0)

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

สายผูกพัน

Dim x As Object
Set x = CreateObject("Scripting.FileSystemObject")
Debug.Print x.GetSpecialFolder(0)

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

ดังนั้นข้อเสียของการรวมล่าช้าคือคุณไม่มีการตรวจสอบที่รัดกุมใด ๆ ที่นี่ แต่นั่นก็เป็นข้อได้เปรียบ - สมมติว่าคุณมีองค์ประกอบที่มีหลายรุ่นและแต่ละรุ่นที่ใหม่กว่ามีฟังก์ชั่นเพิ่มเติมบางอย่าง (ตัวอย่างในโลกแห่งความจริงคือองค์ประกอบ MS Office เช่นอินเทอร์เฟซ Excel COM) การรวมภายหลังช่วยให้คุณสามารถเขียนรหัสที่ทำงานร่วมกับเวอร์ชันทั้งหมดได้ - คุณสามารถกำหนดเวอร์ชันคอมโพเนนต์เฉพาะได้ก่อนและหากคุณพบว่าคุณมี มีเฉพาะรุ่นที่เก่ากว่าเท่านั้นให้หลีกเลี่ยงการเรียกใช้ฟังก์ชันที่ไม่สามารถใช้กับเวอร์ชั่นนั้นได้


-2

บางทีตัวอย่างที่พบบ่อยที่สุดของการเชื่อมล่าช้าคือการแก้ไข URL ของอินเทอร์เน็ต รองรับระบบไดนามิกและระบบขนาดใหญ่โดยไม่ต้องพยายามเชื่อมโยงและเชื่อมโยงทุกไซต์ในโลกก่อนที่คุณจะสามารถเข้าถึงได้ แต่ในทางกลับกันจะเกิดค่าใช้จ่ายบางอย่าง (การค้นหา DNS, การกำหนดเส้นทาง IP น้อยกว่า) ที่รันไทม์

จากแสงดังกล่าวการผูกมัดส่วนใหญ่ในสภาพแวดล้อมทางภาษาจะเร็วหรือน้อยกว่าในเวลารวบรวมหรือเวลาลิงก์

แต่ละชนิดมีต้นทุนและผลประโยชน์


คุณสามารถอ้างอิงเว็บไซต์สำหรับคำจำกัดความการผูกพันนี้ได้หรือไม่? ฉันไม่เคยได้ยินการแก้ไขที่อยู่อินเทอร์เน็ตเป็น "การเชื่อมโยง" แม้ว่าการเชื่อมโยงเป็นการกระทำของการแก้ไขชื่อฉันคิดว่ามีบางคนแย้งว่าแนวคิดของการรวมก่อนหน้า / ล่าช้าสามารถใช้กับการแก้ไข URI กับที่อยู่อินเทอร์เน็ตได้ แต่นี่ไม่ใช่การตีความทั่วไปและแนวคิดของการรวมก่อนหน้า / ปลายจะมาก่อนเวลาที่คอมพิวเตอร์เชื่อมต่อกับอินเทอร์เน็ตโดยทั่วไป
Jay Elston
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.