Microsoft สร้างแอสเซมบลีที่มีการอ้างอิงแบบวงกลมได้อย่างไร


107

ใน. NET BCL มีการอ้างอิงแบบวงกลมระหว่าง:

  • System.dll และ System.Xml.dll
  • System.dll และ System.Configuration.dll
  • System.Xml.dll และ System.Configuration.dll

นี่คือภาพหน้าจอจาก. NET Reflector ที่แสดงความหมาย:

ใส่คำอธิบายภาพที่นี่

วิธีที่ Microsoft สร้างชุดประกอบเหล่านี้เป็นเรื่องลึกลับสำหรับฉัน จำเป็นต้องมีกระบวนการรวบรวมพิเศษเพื่ออนุญาตสิ่งนี้หรือไม่ ฉันนึกว่ามีอะไรน่าสนใจเกิดขึ้นที่นี่


2
คำถามที่ดีมาก ฉันไม่เคยใช้เวลาในการตรวจสอบสิ่งนี้จริง ๆ แต่ฉันอยากรู้คำตอบ อันที่จริงดูเหมือนว่า Dykam ได้ให้สิ่งที่สมเหตุสมผล
Noldorin

3
ทำไม dll เหล่านั้นถึงไม่รวมกันเป็นหนึ่งถ้าพวกเขาต้องการกันและกัน? มีเหตุผลที่เป็นประโยชน์สำหรับสิ่งนั้นหรือไม่?
Andreas Petersson

1
คำถามที่น่าสนใจ ... ฉันอยากรู้คำตอบของ Eric Lippert สำหรับคำถามนี้! และอย่างที่ Andreas พูดฉันสงสัยว่าทำไมพวกเขาไม่ใส่ทุกอย่างในชุดประกอบเดียวกัน ...
Thomas Levesque

หากจำเป็นต้องอัปเดตแอสเซมบลีหนึ่งชุดก็ไม่จำเป็นต้องสัมผัสกับชุดประกอบอื่น นั่นคือเหตุผลเดียวที่ฉันเห็น คำถามที่น่าสนใจ
Atmocreations

2
ดูงานนำเสนอนี้ (ไฟล์ asmmeta): msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad Afshari

คำตอบ:


58

ฉันบอกได้แค่ว่า Mono Project ทำสิ่งนี้อย่างไร ทฤษฎีบทค่อนข้างเรียบง่ายแม้ว่าจะทำให้รหัสยุ่งเหยิง

พวกเขารวบรวม System.Configuration.dll เป็นครั้งแรกโดยไม่มีส่วนที่ต้องการการอ้างอิงถึง System.Xml.dll หลังจากนี้พวกเขารวบรวม System.Xml.dll ตามปกติ ตอนนี้มาถึงความมหัศจรรย์ พวกเขาคอมไพล์ System.configuration.dll ใหม่โดยส่วนที่ต้องการการอ้างอิงถึง System.Xml.dll ขณะนี้มีการรวบรวมที่ประสบความสำเร็จด้วยการอ้างอิงแบบวงกลม

ในระยะสั้น:

  • A ถูกรวบรวมโดยไม่มีรหัสที่ต้องการ B และการอ้างอิงถึง B
  • B ถูกรวบรวม
  • คอมไพล์ใหม่

1
Visual Studio ถูกบล็อก แต่สามารถทำได้โดยใช้คอมไพเลอร์บรรทัดคำสั่ง (csc.exe) โดยตรง ดูคำตอบของฉัน
Alfred Myers

14
ฉันรู้ว่า. ระบบสร้างหลักของ Mono ไม่ใช่ Visual Studio เดาว่า Microsofts ไม่ใช่เช่นกัน
Dykam

35

RBarryYoung และ Dykam กำลังเข้าสู่บางสิ่ง Microsoft ใช้เครื่องมือภายในซึ่งใช้ ILDASM ในการถอดชุดประกอบดึงข้อมูลภายใน / ส่วนตัวและเนื้อหาของวิธีการทั้งหมดออกแล้วคอมไพล์ IL อีกครั้ง (โดยใช้ ILASM) เป็นสิ่งที่เรียกว่า 'dehydrated assembly' หรือ metadata assembly สิ่งนี้ทำได้ทุกครั้งที่เปลี่ยนอินเทอร์เฟซสาธารณะของแอสเซมบลี

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


1
คำตอบที่น่าสนใจคุณมีลิงค์หรือไม่?
Henk Holterman

ฉันกำลังพยายามค้นหาข้อมูลอ้างอิงภายนอกของเครื่องมือ ฉันไม่คิดว่าจะมีการเผยแพร่ภายนอก Microsoft แต่แนวคิดนั้นง่ายมาก: ถอดแยกชิ้นส่วนภายใน - ประกอบใหม่
Srdjan Jovcic

ตกลง - คำตอบที่น่าสนใจ บางลิงค์เพื่อสำรองข้อมูลนี้จะดี
Drew Noakes

ใช่นั่นเป็นวิธีที่ทำ (จากประสบการณ์ส่วนตัว)
Pavel Minaev

1
พวกเขาจะไม่ได้รับการเซ็นชื่ออย่างจริงจังจนกว่าจะสร้างเสร็จ (มีการเซ็นชื่อล่าช้า) ดังนั้นจึงไม่มีการเซ็นแอสเซมบลีที่ขาดน้ำ
Srdjan Jovcic

26

สามารถทำได้ตามที่ Dykam อธิบายไว้ แต่ Visual Studio บล็อกคุณไม่ให้ทำ

คุณจะต้องใช้คอมไพเลอร์บรรทัดคำสั่ง csc.exe โดยตรง

  1. csc / target: ไลบรารี ClassA.cs

  2. csc / target: ไลบรารี ClassB.cs /reference:ClassA.dll

  3. csc / target: ไลบรารี ClassA.cs ClassC.cs /reference:ClassB.dll


//ClassA.cs
namespace CircularA {
    public class ClassA {
    }
}


//ClassB.cs
using CircularA;
namespace CircularB {
    public class ClassB : ClassA  {
    }
}


//ClassC.cs
namespace CircularA {
    class ClassC : ClassB {
    }
}

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

เท่าที่ฉันรู้ไม่สามารถทดสอบได้ที่นี่
Dykam

ฉันอยากจะเห็นสิ่งนั้นจริงๆ จากสิ่งที่ฉันทดลองที่นี่ในขณะที่คุณพยายามเพิ่มการอ้างอิง IDE จะหยุดคุณ
Alfred Myers

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

มันเหมือนกับคำตอบของ Srdjan แม้ว่าจะเป็นวิธีการอื่น
Dykam

18

มันค่อนข้างง่ายที่จะทำใน Visual Studio ตราบใดที่คุณไม่ได้ใช้การอ้างอิงโครงการ ... ลองสิ่งนี้:

  1. เปิด Visual Studio
  2. สร้างโครงการห้องสมุดคลาส 2 โครงการ "ClassLibrary1" และ "ClassLibrary2"
  3. สร้าง
  4. จาก ClassLibrary1 เพิ่มการอ้างอิงไปยัง ClassLibrary2 โดยเรียกดู dll ที่สร้างในขั้นตอนที่ 3
  5. จาก ClassLibrary2 เพิ่มการอ้างอิงไปยัง ClassLibrary1 โดยเรียกดู dll ที่สร้างในขั้นตอนที่ 3
  6. สร้างอีกครั้ง (หมายเหตุ: หากคุณทำการเปลี่ยนแปลงในทั้งสองโครงการคุณจะต้องสร้างสองครั้งเพื่อให้การอ้างอิงทั้งสอง "ใหม่")

นี่คือวิธีที่คุณทำ แต่อย่างจริงจัง ... คุณไม่เคยทำในโครงการจริง! ถ้าคุณทำเช่นนั้นซานต้าจะไม่นำของขวัญมาให้คุณในปีนี้


1
ข้อยกเว้นเพียงอย่างเดียวคือในระหว่างวันที่ 26-31 ธันวาคมและของขวัญได้รับการประกันเรียบร้อยแล้ว
Jesse Hufstetler

6

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


4

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

ดังนั้นหากคุณมีสองไลบรารี A และ B ที่ต้องอ้างอิงซึ่งกันและกันให้ลองทำดังนี้

  1. ลิงก์ A โดยไม่มีการอ้างถึง B
  2. ลิงก์ B พร้อมอ้างอิงถึง A
  3. ลิงก์ A เพิ่มการอ้างอิงถึง B

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


ทฤษฎีบทนั้นถูกต้อง อย่างไรก็ตามในโลก. Net การเชื่อมโยงทำได้แบบไดนามิกไม่ใช่ปัญหา เป็นขั้นตอนการรวบรวมที่จำเป็นต้องใช้โซลูชันนี้
Dykam

ขออภัยที่ต้องแก้ไขอีกครั้ง: P. แต่การอ้างอิง (การเชื่อมโยง) ในเวลาคอมไพล์เกิดขึ้นในโลก. Net ซึ่งเป็นทุกสิ่งที่มาจากข้อมูลจำเพาะของ ECMA นั้น ดังนั้น Mono, dotGnu และ. Net ไม่ใช่ Windows เอง
Dykam

1

แนวทางหนึ่งที่เป็นไปได้คือการใช้การคอมไพล์ตามเงื่อนไข (#if) เพื่อคอมไพล์ System.dll ก่อนที่ไม่ขึ้นอยู่กับแอสเซมบลีอื่น ๆ จากนั้นคอมไพล์แอสเซมบลีอื่น ๆ และในที่สุด recompile System.dll เพื่อรวมส่วนต่างๆขึ้นอยู่กับ Xml และ การกำหนดค่า


1
น่าเสียดายที่สิ่งนี้ไม่อนุญาตให้คุณอ้างอิงแอสเซมบลีแบบมีเงื่อนไข (ฉันหวังว่ามันจะเป็นไปได้มันจะช่วยในโปรเจ็กต์ของฉันได้จริงๆ ... )
Thomas Levesque

1
การอ้างอิงแบบมีเงื่อนไขสามารถทำได้อย่างง่ายดายโดยการแก้ไขไฟล์. csproj เพียงเพิ่มแอตทริบิวต์ Condition ให้กับองค์ประกอบ <Reference>
Daniel

0

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


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