Лучше написать библиотеку .NET с учетом ограничений COM или отделить библиотеку .NET от Interop?

9

Я наткнулся на эту интересную статью: как я полюбил COM-совместимость в CodeProject, и это заставило меня задуматься ...

Автор утверждает, что они не хотят никаких COM-объектов в своей библиотеке .NET, потому что это лишает красоту их библиотеки .NET. Вместо этого они предпочли бы написать отдельную библиотеку Interop, которая предоставляет свою библиотеку .NET для COM. Эта библиотека взаимодействия могла бы обрабатывать тот факт, что COM не поддерживает конструкторы с параметрами, перегруженными методами, обобщенными типами, наследованием, статическими методами и т. Д.

И хотя я думаю, что это очень ново, разве это не просто сборка проекта?

  • Теперь вам нужно выполнить модульное тестирование вашей библиотеки .NET И библиотеки Interop.
  • Теперь вам нужно потратить время на выяснение того, как обойти вашу прекрасную библиотеку .NET и открыть ее для COM.
  • Вы должны, по сути, удвоить или утроить количество ваших классов.

Я, конечно, могу понять, нужна ли вам ваша библиотека для поддержки как COM, так и не COM. Однако, если вы собираетесь использовать только COM, приносит ли такой дизайн какие-то преимущества, которых я не вижу? Вы получаете только преимущества языка C #?

Или это облегчает управление версиями вашей библиотеки, предоставляя вам обертку? Это заставляет ваши модульные тесты работать быстрее, не требуя использования COM?

robodude666
источник
2
Вы имеете в виду, помимо возможности использовать полезные инструменты, чтобы сделать реальный код лучше / чище? И предоставление четкого пути миграции для этого (предположительно унаследованного) COM-материала?
Теластин
3
Это похоже на шаблон фасада, большой по всему пространству имен. Я думаю, что это разумно, если вы хотите использовать функции .NET, но действительно нуждаетесь в OLE-автоматизации. Ваши обертки COM в основном будут преобразовывать современные (неизменяемые? Общие?) Идиомы в более старые объекты COM с помощью конструкторов без параметров, сильно изменяемых объектов и SAFEARRAY для ваших общих списков. Если вы поддерживаете другие компоненты .NET или полностью влюблены в новые стили, это будет иметь смысл. Если вы поддерживаете ТОЛЬКО COM, то почему бы не написать все это на VB6 (32-битном) и оставить только одну идиому?
Майк
3
@MasonWheeler: Это устарело так же, как и все программное обеспечение старше 6 месяцев - «устаревшее», я прав?
whatsisname
2
@MasonWheeler COM нельзя обесценивать, независимо от того, сколько людей (включая некоторые группы в Microsoft) хотели бы этого, потому что это все еще лучший выбор для управления вещами вне процесса.
Майк
2
@ Майк, я на самом деле смотрю на перенос проекта VB6 на C # .NET. Используемые нами пользовательские API-интерфейсы очень просты, однако закулисный код не поддерживается как есть. Я надеюсь реализовать большую часть библиотеки в C # .NET и предоставить Interop Facade, который предоставляет простые API, которые взаимодействуют с различными классами.
robodude666

Ответы:

7

Однако, если вы собираетесь использовать только COM, приносит ли такой дизайн какие-то преимущества, которых я не вижу?

Этот фрагмент кода вашего вопроса наиболее важная часть вашего дизайнерского решения здесь, и ответ (как всегда) , это зависит .

Если вы намереваетесь использовать библиотеку только через COM Interop, вам следует разработать всю систему с учетом этого. Там нет причин, чтобы утроить количество строк. Чем больше LoC в системе, тем больше будет дефектов. Поэтому вы должны проектировать свою систему так, чтобы использовать только COM-совместимые функции.

Однако я не могу придумать вескую причину ограничить использование библиотеки .Net для COM. Почему бы вам не захотеть использовать сборку в другом .Net-коде? Нет смысла ограничивать себя таким образом.

У меня есть некоторый опыт в этом, поэтому я поделюсь, как я справился с этим. Это, конечно, не идеально. Я сделал некоторые компромиссы. Я использую только фасад для COM-классов (действительно интерфейсов), которые несовместимы с более новыми .Net идиомами, в противном случае я напрямую представляю .Net-интерфейс для COM. Это в основном означает, что я использую фасад для любого интерфейса, который использует дженерики. Дженерики - единственная вещь, с которой я столкнулся, просто несовместимы. К сожалению, это означает фасад для всего, что возвращает 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 моего репо. ISourceControlProviderи GitProviderбудет представлять особый интерес.

Резиновая утка
источник
Спасибо за ответ @RubberDuck! Я столкнулся с проектом RubberDuck несколько дней назад; было очень интересно читать код. Вы можете перейти IEnumerableна VBA? Кроме того, Rubberduck.Interop.GitProviderпочему вы определили параметризованные конструкторы, если COM их не поддерживает?
robodude666
Да, вы можете передать IEnumerableсквозной COM, но это должна быть более старая неуниверсальная версия. Что касается конструкторов, взгляните на SourceControlClassFactory. По сути, вы имитируете его, инициализируя экземпляр класса, которому не нужны параметры для своего ctor, и используя его для получения доступа к новым экземплярам классов вашего API.
RubberDuck
Я предполагаю, что что-либо определенное в ваших ComVisible(true)классах / интерфейсах, которое COM не поддерживает, просто "игнорируется"? Я также предполагаю, что если у вас есть класс с одним и тем же именем, определенный в нескольких пространствах имен, вам нужно предоставить уникальный, ProgIdесли вы хотите предоставить более одного из них?
robodude666
Да. Это верно, и staticметоды игнорируются. Я пытаюсь увидеть, есть ли в наличии эквивалент VB6 VB_PredeclaredId. Я думаю , это может быть возможно создать экземпляр по умолчанию.
RubberDuck
7

это лишает красоты их библиотеки .NET

Этот автор нуждается в пробуждении пощечину. Цель любого профессионального проекта - создать работающее программное обеспечение, которое люди хотят использовать. Любые ошибочные идеалы о красоте появляются намного позже. Если это их единственное рассуждение, автор потерял все доверие.

Возможность помечать ваши классы как видимые и иметь возможность общаться с вашим .NET-кодом через COM чрезвычайно полезна. Отказ от этого огромного преимущества для красоты - это безумие.

Тем не менее, для того, чтобы все работало с COM, нужно найти некоторые компромиссы, один из которых - использование конструктора без аргументов, а также некоторые другие вещи, которые вы упомянули. Если это те функции, которые вы в любом случае не используете, или вам не помешает их не использовать, тогда нужно будет проделать большую работу, используя один и тот же класс для COM и non com.

С другой стороны, если ваши COM-клиенты ожидают кардинально другого интерфейса, имеют зависимости или другие ограничения, с которыми приходится сталкиваться в ваших классах .NET, или вы ожидаете существенного изменения ваших классов .NET и вам необходимо поддерживать COM в обратном направлении. совместимость, то непременно создайте класс Interop для преодоления этого разрыва.

Но не думай о красоте. Возможность выставить COM через .NET предназначена для сохранения вашей работы. Не создавайте больше работы для себя.

как зовут
источник
+1. Хотя я люблю красоту черных кнопок на черном ящике, практичность значительно важнее.
gbjbaanb
На самом деле, человеку, который ввел термин «красота» в это обсуждение, может понадобиться пробуждение, и это был не автор этой другой статьи.
Док Браун
2
Просто примечание: если вашим текущим конструкторам требуются аргументы, лучше предоставить фабрику классов для ваших компонентов COM, а не перепроектировать существующий интерфейс / API.
RubberDuck
1
Возможно ли выставить вашу фабрику как класс "GlobalMultiUse"? Таким образом, вы можете просто обойтись Set oObject = MyCOMInterop.NewMyClass()без необходимости сначала создавать новый объект Factory?
robodude666
Это чертовски хороший вопрос @ robodude666. Теперь, когда вы упомянули об этом, я надеюсь на это.
RubberDuck
3

Что ж, если честно, первоначальный автор этой статьи нигде в своей статье не использовал слово «красота», то есть вы, что могло бы дать необъективное впечатление тем, кто не нашел время, чтобы прочитать статью самих себя.

Насколько я понял эту статью, библиотека .NET уже существовала и была написана без учета COM - вероятно, потому что во время создания библиотеки не было необходимости поддерживать COM.

Позже было введено дополнительное требование поддержки COM. Столкнувшись с такой ситуацией, у вас есть два варианта:

  • переработать оригинальную библиотеку, чтобы сделать ее совместимой с COM-взаимодействием (с риском поломки)

  • пусть исходная рабочая библиотека в основном как есть, и вместо этого создайте отдельную сборку с функцией «обертки» (или «адаптера»)

Создание оберток или адаптеров - это хорошо известный шаблон проектирования (или, в данном случае, скорее архитектурный шаблон), и за и против также хорошо известны. Одним из аргументов в пользу этого является лучшее «разделение интересов», и это не имеет ничего общего с красотой.

К вашей заботе

Теперь вам нужно выполнить модульное тестирование вашей библиотеки .NET И библиотеки Interop.

Когда вы вводите свой COM-интерфейс в существующую библиотеку .NET путем редизайна, вы должны также протестировать этот интерфейс. Конечно, для введения написанного вручную адаптера могут потребоваться некоторые дополнительные тесты, чтобы интерфейс работал, но нет необходимости копировать все модульные тесты основной бизнес-функциональности библиотеки. Не забывайте, что это просто еще один интерфейс для lib.

Теперь вам нужно потратить время на выяснение того, как обойти вашу прекрасную библиотеку .NET и открыть ее для COM.

Когда вам нужно переделать оригинальную библиотеку, вы тоже должны это понять, и я думаю, что это будет гораздо больше работы.

Вы должны, по сути, удвоить или утроить количество ваших классов.

Только для классов, предоставляемых через общедоступный интерфейс COM, который должен быть небольшим количеством частей классов исходной библиотеки.

Сказал, что для другой ситуации, которую вы упомянули, когда вы уже с самого начала знаете, что должны поддерживать COM как первоклассного гражданина, я думаю, что вы правы: нужно избавиться от хлопот, связанных с добавлением отдельной библиотеки адаптера, и спроектировать .NET lib с поддержкой COM напрямую.

Док Браун
источник
(+1) Однако, «... которое должно быть небольшим количеством ... исходной библиотеки», только иногда верно. Существует множество типов проектов, разработанных в «стиле библиотеки классов», где один класс соответствует одной общедоступной цели, и эти классы объединяются для создания интересной функциональности. В этом случае между счетчиками классов .NET и счетчиками классов взаимодействия COM может быть взаимно-однозначное соответствие.
Ронг