CLSCompliant (true) перетаскивает в неиспользованные ссылки - PullRequest
29 голосов
/ 10 августа 2009

Может ли кто-нибудь объяснить следующее поведение?

В итоге, если вы создаете несколько CLS-совместимых библиотек в Visual Studio 2008 и имеете общий корень пространства имен, для библиотеки, ссылающейся на другую библиотеку, потребуется ссылки на ссылки этой библиотеки. даже если он их не потребляет.

Довольно сложно объяснить в одном предложении, но вот шаги для воспроизведения поведения (обратите особое внимание на пространства имен):

Создайте библиотеку с именем LibraryA и добавьте в нее отдельный класс:

namespace Ploeh
{
    public abstract class Class1InLibraryA
    {
    }
}

Убедитесь, что библиотека совместима с CLS, добавив [assembly: CLSCompliant(true)] в AssemblyInfo.cs.

Создайте еще одну библиотеку с именем LibraryB и ссылку на LibraryA. Добавьте следующие классы в LibraryB:

namespace Ploeh.Samples
{
    public class Class1InLibraryB : Class1InLibraryA
    {
    }
}

и

namespace Ploeh.Samples
{
    public abstract class Class2InLibraryB
    {
    }
}

Убедитесь, что LibraryB также совместима с CLS.

Обратите внимание, что Class1InLibraryB происходит от типа в LibraryA, а Class2InLibraryB - нет.

Теперь создайте третью библиотеку с именем LibraryC и ссылку на LibraryB (но не LibraryA). Добавьте следующий класс:

namespace Ploeh.Samples.LibraryC
{
    public class Class1InLibraryC : Class2InLibraryB
    {
    }
}

Это все равно должно скомпилироваться. Обратите внимание, что Class1InLibraryC происходит от класса в LibraryB, что не использует никаких типов из LibraryA .

Также обратите внимание, что Class1InLibraryC определен в пространстве имен, которое является частью иерархии пространств имен, определенной в LibraryB.

Пока что LibraryC не имеет ссылки на LibraryA, и, поскольку он не использует типы из LibraryA, решение компилируется.

Теперь сделайте LibraryC CLS-совместимым. Внезапно решение больше не компилируется, и появляется сообщение об ошибке:

Тип 'Ploeh.Class1InLibraryA' определен в сборке, на которую нет ссылок. Вы должны добавить ссылку на сборку «Ploeh, версия = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null».

Вы можете снова скомпилировать решение одним из следующих способов:

  • Удалите соответствие CLS из библиотеки C
  • Добавить ссылку на LibraryA (хотя она вам не нужна)
  • Измените пространство имен в LibraryC, чтобы оно не входило в иерархию пространства имен LibraryB (например, в Fnaah.Samples.LibraryC)
  • Измените пространство имен Class1InLibraryB (то есть то, которое не используется из LibracyC), чтобы оно не входило в иерархию пространства имен LibraryC (например, на Ploeh.Samples.LibraryB)

Кажется, что существует некоторая странная взаимосвязь между иерархией пространства имен и соответствием CLS.

Решить эту проблему можно, выбрав один из вариантов в списке выше, но кто-нибудь может объяснить причину , стоящую за этим поведением?

Ответы [ 3 ]

19 голосов
/ 10 августа 2009

Я просмотрел официальные документы для CLS (http://msdn.microsoft.com/en-us/netframework/aa569283.aspx),, но моя голова взорвалась, прежде чем я смог найти простой ответ.

Но я думаю, что основа заключается в том, что компилятор, чтобы проверить соответствие CLS библиотеки LibraryC, должен изучить возможные конфликты именования с библиотекой A.

Компилятор должен проверить все «части типа, которые доступны или видимы за пределами определяющей сборки» (правило 1 CLS).

Поскольку открытый класс Class1InLibraryC наследует Class2InLibraryB, он должен также проверить соответствие CLS для LibraryA, в частности потому, что «Ploeh. *» Теперь находится «в области действия» для правила 5 CLS. «Все имена, введенные в CLS-совместимой области действия, должны быть независимым от рода ".

Изменение пространства имен Class1InLibraryB или Class1InLibraryC, чтобы они стали различимыми, похоже, убеждает компилятор, что больше нет шансов для конфликта имен.

Если вы выберете option (2), добавите ссылку и скомпилируете, вы увидите, что ссылка на самом деле не помечена в результирующих метаданных сборки, так что это только зависимость времени компиляции / проверки.

7 голосов
/ 10 августа 2009

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

Следуя некоторым рекомендациям, CLS-совместимый код должен следовать:

  • Избегайте использования имен, обычно используемых в качестве ключевых слов в языках программирования.
  • Не ожидайте, что пользователи платформы смогут создавать вложенные типы.
  • Предположим, что реализации методов с одинаковыми именами и сигнатурами на разных интерфейсах независимы.

Правила определения соответствия CLS:

  • Когда сборка не содержит явного атрибута System.CLSCompliantAttribute, она должна быть предполагается, что он содержит System.CLSCompliantAttribute (false).
  • По умолчанию тип наследует атрибут соответствия CLS своего включающего типа (для вложенных типов) или получает уровень соответствия, прикрепленный к его сборке (для типов верхнего уровня).
  • По умолчанию другие члены (методы, поля, свойства и события) наследуют CLS-соответствие своего типа.

Теперь, что касается компилятора, (правило 1 CLS), он должен иметь возможность применять правила для соответствия CLS к любой информации, которая будет экспортирована за пределы сборки, и считает, что тип является CLS-совместимым, если все его общедоступные части (те классы, интерфейсы, методы, поля, свойства и события, которые доступны для кода, выполняющегося в другой сборке) либо

  • имеют подписи, состоящие только из CLS-совместимых типов, или
  • специально помечены как не соответствующие CLS.

По правилам CTS область действия - это просто группа / коллекция имен, и внутри области имя может относиться к нескольким объектам, если они имеют разные виды (методы, поля, вложенные типы, свойства, события) или имеют разные подписи. Именованная сущность имеет свое имя ровно в одной области, поэтому для идентификации этой записи необходимо применять как область, так и имя. Область действия уточняет имя.

Поскольку типы именуются, имена типов также группируются по областям. Чтобы полностью идентифицировать тип, имя типа должно быть квалифицировано областью. Имена типов ограничены сборкой, в которой содержится реализация этого типа.

Для областей, совместимых с CLS, все имена должны отличаться независимо от вида, за исключением случаев, когда имена идентичны и разрешаются путем перегрузки. Другими словами, хотя CTS позволяет одному типу использовать одно и то же имя для поля и метода, CLS этого не делает (правило 5 CLS).

Если сделать еще один шаг вперед, CLS-совместимый тип не должен требовать реализации несовместимых с CLS типов (правило 20 CLS), а также наследовать от другого типа жалобы CLS (правило 23 CLS).

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

  • Все ссылки на другие сборки разрешаются под контролем текущей области сборки.
  • Всегда можно определить, в какой области сборки выполняется конкретная реализация. Все запросы, исходящие из этой области сборки, разрешаются относительно этой области.

В конечном итоге это означает, что для проверки соответствия CLS типа, компилятор должен иметь возможность проверить, что все открытые части этого типа также совместимы с CLS. Это означает, что оно должно гарантировать, что имя является уникальным в пределах области, что оно не зависит от несовместимых с CLS типов для частей своей собственной реализации и что оно наследуется от других типов, также совместимых с CLS. Единственный способ сделать это - изучить все сборки, на которые ссылается тип.

Помните, что этап сборки в Visual Studio - это, по сути, оболочка GUI для выполнения MSBuild, которая в конечном итоге является не чем иным, как скриптовым способом вызова компилятора командной строки C #. Чтобы компилятор мог проверить CLS-соответствие типа, он должен знать и уметь находить все сборки, на которые ссылаются типы (а не проект). Поскольку он вызывается через MSBuild и, в конечном счете, Visual Studio, единственный способ для Visual Studio (MSBuild) сообщить ему об этих сборках - включить их в качестве ссылок.

Очевидно, что поскольку компилятор может выяснить, что он «пропускает» ссылки для проверки соответствия CLS и успешной компиляции, было бы неплохо, если бы он просто включил эти ссылки автоматически от нашего имени. Проблема здесь заключается в определении , какую версию сборки включить и , где эта сборка находится в файловой системе. Вынуждая разработчика предоставлять эту информацию, компилятор помогает гарантировать, что ему предоставлена ​​правильная информация. Это также имеет побочный эффект, заключающийся в том, что все зависимые сборки копируются в папки Debug/bin или Release/bin во время сборки, поэтому они находятся в правильном каталоге при запуске приложения после его компиляции.

1 голос
/ 24 июля 2014

Проблема устранена в Roslyn, который доступен в Visual Studio 14.
По состоянию на июль 2014 года текущая ОСАГО доступна здесь .
См. этот отчет об ошибке для подробностей.

...