ดีกว่าที่จะเขียนไลบรารี. NET ของคุณด้วยข้อ จำกัด COM ในใจหรือแยกไลบรารี. NET ของคุณออกจาก Interop


9

ฉันเจอบทความที่น่าสนใจนี้: ฉันจะรัก COM การทำงานร่วมกันใน CodeProject ได้อย่างไรซึ่งทำให้ฉันคิดว่า ...

ผู้เขียนระบุว่าพวกเขาไม่ต้องการ COM-ities ใด ๆ ในไลบรารี. NET ของพวกเขาเพราะมันไม่ได้อยู่ในความสวยงามของไลบรารี. NET ของพวกเขา แต่พวกเขาต้องการเขียนไลบรารี Interop แยกที่แสดงไลบรารี. NET ของพวกเขาไปที่ COM แทน ไลบรารี Interop นี้จะจัดการกับความจริงที่ว่า COM ไม่สนับสนุนคอนสตรัคเตอร์ที่มีพารามิเตอร์

และในขณะที่ฉันคิดว่ามันเป็นเรื่องแปลกใหม่มันไม่เพียงรวบรวมโครงการหรือไม่

  • ตอนนี้คุณต้องทำการทดสอบหน่วย. ไลบรารี NET ของคุณและไลบรารี Interop
  • ตอนนี้คุณต้องใช้เวลาในการหาวิธีการทำงานกับไลบรารี. NET ที่สวยงามของคุณและเปิดให้ COM
  • คุณต้องนับจำนวนชั้นเรียนอย่างมีประสิทธิภาพสองหรือสามเท่า

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

หรือว่าจะทำให้เวอร์ชันไลบรารีของคุณง่ายขึ้นด้วยการเตรียมเสื้อคลุมให้คุณ มันทำให้การทดสอบหน่วยของคุณทำงานได้เร็วขึ้นโดยไม่ต้องการใช้ COM หรือไม่


2
คุณหมายถึงนอกจากความสามารถในการใช้เครื่องมือที่มีประโยชน์เพื่อทำให้โค้ดจริงดีขึ้น / สะอาดขึ้นแล้ว? และให้เส้นทางการย้ายข้อมูลที่ชัดเจนสำหรับสิ่ง COM (น่าจะเป็นแบบดั้งเดิม)
Telastyn

3
นี่เป็นเหมือนรูปแบบซุ้มเขียนใหญ่กว่าเนมสเปซทั้งหมด ฉันคิดว่ามันฉลาดในการจัดระเบียบเช่นนี้หากคุณต้องการใช้คุณสมบัติ. NET แต่ต้องการ OLE Automation จริงๆ เครื่องห่อ COM ของคุณโดยทั่วไปจะแปลงสำนวนที่ทันสมัย ​​(ไม่เปลี่ยนรูปแบบ? ทั่วไป?) เป็นวัตถุ COM รุ่นเก่าที่มีตัวสร้างแบบไม่มีพารามิเตอร์, วัตถุที่ไม่แน่นอนอย่างหนักและ SAFEARRAYs สำหรับรายการทั่วไปของคุณ หากคุณสนับสนุนส่วนประกอบอื่น ๆ ของ. NET หรือชื่นชอบกับรูปแบบใหม่ทั้งหมดนี้ก็สมเหตุสมผลแล้ว หากคุณรองรับเฉพาะ COM แล้วทำไมไม่ลองเขียนสิ่งทั้งหมดใน VB6 (32 บิต) และใช้สำนวนเดียวล่ะ?
Mike

3
@ MasonWheeler: มันเลิกในลักษณะเดียวกันซอฟต์แวร์ทั้งหมดที่เก่ากว่า 6 เดือนคือ "legacy" ใช่ไหม?
whatsisname

2
@MasonWheeler COM ไม่สามารถคิดค่าเสื่อมราคาได้ไม่ว่าผู้คนจำนวนมาก (รวมถึงบางกลุ่มใน Microsoft) จะต้องการมันเพราะยังคงเป็นตัวเลือกที่ดีที่สุดในการควบคุมสิ่งต่าง ๆ ที่อยู่นอกกระบวนการ
Mike

2
@ ไมค์ฉันจริง ๆ แล้วมองไปที่การย้ายโครงการ VB6 เป็น C # .NET API ที่ผู้ใช้ใช้ซึ่งเราใช้นั้นง่ายมากอย่างไรก็ตามโค้ดเบื้องหลังไม่สามารถบำรุงรักษาได้เหมือนเดิม ฉันหวังว่าจะติดตั้งไลบรารี่ส่วนใหญ่ใน C # .NET และจัดให้มี Interop Facade ที่ให้ API ง่าย ๆ ที่โต้ตอบกับคลาสต่างๆ
robodude666

คำตอบ:


7

อย่างไรก็ตามหากคุณต้องการใช้ COM เพียงอย่างเดียวการออกแบบประเภทนี้ให้ประโยชน์ที่ฉันไม่เห็นหรือไม่

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

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

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

ฉันมีประสบการณ์เกี่ยวกับเรื่องนี้ดังนั้นฉันจะแบ่งปันวิธีจัดการกับมัน มันไม่เหมาะอย่างแน่นอน ฉันได้ทำการแลกเปลี่ยนบางอย่าง ฉันใช้ซุ้มสำหรับคลาส COM เท่านั้น (อินเทอร์เฟซจริง ๆ ) ที่เข้ากันไม่ได้กับสำนวน. Net รุ่นใหม่มิฉะนั้นฉันจะแสดงอินเตอร์เฟส. Net กับ COM โดยตรง นี่หมายความว่าฉันใช้ facade สำหรับอินเทอร์เฟซที่ใช้ generics ยาชื่อสามัญเป็นสิ่งเดียวที่ฉันเจอซึ่งไม่เข้ากัน น่าเสียดายที่นี่หมายถึงซุ้มสำหรับสิ่งใดก็ตามที่ส่งคืนIEnumerable<T>ซึ่งเป็นเรื่องธรรมดา

อย่างไรก็ตามมันก็ไม่ได้เลวร้ายอย่างที่คิดเพราะคลาสซุ้มของคุณสืบทอดมาจากคลาส. Net ของคุณและใช้อินเทอร์เฟซ Interop เฉพาะ บางสิ่งเช่นนี้

namespace Company.Product.Feature
{
    public class MyClass : INetInterface 
    {
        // lots of code
    }
}

namespace Company.Product.Interop
{
    public class MyClass : Feature.MyClass, IComInterface
    {
        // over ride only the  incompatible properties/methods
    }
}

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

สำหรับตัวอย่างที่สมบูรณ์แบบนี้คุณสามารถเรียกดูผ่านโฟลเดอร์SourceControlและInteropของ repo ของฉัน ISourceControlProviderและGitProviderจะเป็นที่สนใจโดยเฉพาะ


ขอบคุณสำหรับการตอบกลับ @RubberDuck! ฉันวิ่งข้ามโครงการ RubberDuck เมื่อไม่กี่วันที่ผ่านมา มันน่าสนใจมาก ๆ ในการอ่านรหัส คุณสามารถผ่านIEnumerableไปยัง VBA ได้หรือไม่ นอกจากนี้ในRubberduck.Interop.GitProviderเหตุผลที่คุณกำหนด constructors แบบกำหนดพารามิเตอร์ถ้า COM ไม่สนับสนุนพวกเขา
robodude666

ใช่คุณสามารถส่งIEnumerableผ่าน COM แต่ต้องเป็นรุ่นที่เก่ากว่าไม่ใช่แบบทั่วไป SourceControlClassFactoryสำหรับการก่อสร้างที่ใช้เวลาดูที่ โดยพื้นฐานแล้วคุณปลอมมันด้วยการเริ่มต้นอินสแตนซ์ของคลาสที่ไม่ต้องการพารามิเตอร์สำหรับ ctor และใช้เพื่อเข้าถึงอินสแตนซ์ใหม่ของคลาส API ของคุณ
RubberDuck

ฉันคาดเดาว่ามีสิ่งใดที่กำหนดไว้ในComVisible(true)คลาส / อินเทอร์เฟซของคุณที่ COM ไม่รองรับนั้นเป็นเพียง "ละเว้น"? ฉันเดาด้วยถ้าคุณมีคลาสชื่อเดียวกันที่กำหนดในเนมสเปซหลายรายการคุณจะต้องระบุชื่อเฉพาะProgIdหากคุณต้องการแสดงมากกว่าหนึ่งรายการหรือไม่
robodude666

ใช่. ถูกต้องและstaticวิธีการจะถูกละเว้น ฉันพยายามที่จะดูว่ามีเทียบเท่ากับ VB6 ที่VB_PredeclaredIdมีอยู่ ฉันคิดว่าอาจเป็นไปได้ที่จะสร้างอินสแตนซ์เริ่มต้น
RubberDuck

7

มันใช้เวลาห่างจากความสวยงามของ. NET

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

ความสามารถในการทำเครื่องหมายคลาสของคุณเป็น com-visible และความสามารถในการพูดคุยกับรหัส. NET ของคุณผ่าน COM นั้นมีประโยชน์อย่างมาก การละทิ้งว่าผลดีต่อการผลิตเพื่อความงามนั้นบ้า

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

ในทางกลับกันถ้าไคลเอนต์ COM ของคุณคาดว่าอินเทอร์เฟซที่แตกต่างกันอย่างมากมีการอ้างอิงหรือข้อ จำกัด อื่น ๆ ที่น่ารังเกียจที่จะจัดการกับในชั้นเรียน. NET ของคุณหรือคุณคาดว่าจะเปลี่ยนคลาส. NET ของคุณอย่างมีนัยสำคัญ ความเข้ากันได้จากนั้นทั้งหมดสร้างคลาส Interop เพื่อเชื่อมช่องว่างนั้น

แต่อย่าคิดถึง "ความงาม" ความสามารถในการเปิดเผย COM ผ่าน. NET มีไว้เพื่อช่วยให้คุณประหยัด อย่าสร้างงานให้ตัวเองมากขึ้น


+1 ในขณะที่ฉันรักความสวยงามของปุ่มสีดำบนกล่องดำที่มีความสำคัญมากกว่า
gbjbaanb

อันที่จริงผู้ที่แนะนำคำว่า "ความงาม" ในการสนทนานี้อาจต้องตื่นขึ้นมาและนั่นไม่ใช่ผู้เขียนบทความอื่นนั้น
Doc Brown

2
เพียงหมายเหตุด้านข้างหากผู้สร้างปัจจุบันของคุณต้องการ args เป็นการดีที่สุดที่จะให้โรงงานคลาสสำหรับคอมโพเนนต์ COM ของคุณแทนที่จะออกแบบอินเตอร์เฟส / API ที่มีอยู่ใหม่
RubberDuck

1
เป็นไปได้ไหมที่จะเปิดเผยโรงงานของคุณเป็นคลาส "GlobalMultiUse" ดังนั้นคุณสามารถทำได้Set oObject = MyCOMInterop.NewMyClass()โดยไม่ต้องสร้างวัตถุโรงงานใหม่ก่อน
robodude666

นั่นเป็นคำถามที่ดีมาก @ robodude666 ตอนนี้ที่คุณพูดถึงมันฉันหวังว่าดังนั้น
RubberDuck

3

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

เท่าที่ฉันเข้าใจบทความนั้นห้องสมุด. NET มีอยู่แล้วและถูกเขียนโดยไม่มี COM ในใจ - อาจเป็นเพราะในช่วงเวลาที่ห้องสมุดถูกสร้างขึ้นไม่มีความต้องการที่จะสนับสนุน COM

ต่อมาได้มีการแนะนำข้อกำหนดเพิ่มเติมในการสนับสนุน COM เมื่อเผชิญกับสถานการณ์เช่นนี้คุณมีสองตัวเลือก:

  • ออกแบบ lib ดั้งเดิมใหม่เพื่อให้เข้ากันได้กับ COM interop (โดยมีความเสี่ยงต่อการแตกหัก)

  • ให้ไลบรารี่การทำงานส่วนใหญ่เป็นแบบเดิมและสร้างแอสเซมบลีแยกต่างหากด้วยฟังก์ชั่น "wrapper" (หรือ "adapter")

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

ถึงความกังวลของคุณ

ตอนนี้คุณต้องทำการทดสอบหน่วย. ไลบรารี NET ของคุณและไลบรารี Interop

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

ตอนนี้คุณต้องใช้เวลาในการหาวิธีการทำงานกับไลบรารี. NET ที่สวยงามของคุณและเปิดให้ COM

เมื่อคุณต้องออกแบบไลบรารีต้นฉบับใหม่คุณต้องเข้าใจด้วยและฉันเดาว่ามันจะทำงานได้มากกว่า

คุณต้องนับจำนวนชั้นเรียนอย่างมีประสิทธิภาพสองหรือสามเท่า

สำหรับคลาสที่เปิดเผยผ่านส่วนต่อประสาน COM สาธารณะซึ่งควรเป็นส่วนเล็ก ๆ ของส่วนคลาสของไลบรารีดั้งเดิม

กล่าวว่าสำหรับสถานการณ์ที่แตกต่างที่คุณพูดถึงเมื่อคุณรู้อยู่แล้วว่าคุณต้องสนับสนุน COM ในฐานะพลเมืองชั้นหนึ่งตั้งแต่เริ่มต้นฉันคิดว่าคุณถูกต้อง: เราควรประหยัดความยุ่งยากในการเพิ่มอะแดปเตอร์แยกและออกแบบ .NET lib พร้อมการสนับสนุน COM โดยตรง


(+1) อย่างไรก็ตาม "... ซึ่งน่าจะเป็นจำนวนน้อย ... ห้องสมุดดั้งเดิม" บางครั้งก็เป็นจริง มีโปรเจ็กต์มากมายหลายประเภทที่ออกแบบใน "class library style" ซึ่งคลาสหนึ่งเท่ากับจุดประสงค์ที่เปิดเผยต่อสาธารณชนและคลาสนั้นประกอบขึ้นด้วยฟังก์ชันการทำงานที่น่าสนใจ ในกรณีดังกล่าวอาจมีการแมปแบบหนึ่งต่อหนึ่งระหว่างการนับคลาส. NET และการนับคลาส COM interop
วงเวียน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.