CLSCompliant(true) перетаскивает в неиспользованные ссылки
Кто-нибудь может объяснить следующее поведение?
Таким образом, если вы создаете несколько совместимых с 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 Соответствие из LibraryC
- Добавьте ссылку на LibraryA (хотя она вам не нужна)
- Измените пространство имен в LibraryC, чтобы оно не входило в иерархию пространства имен LibraryB (например, Fnaah.Samples.LibraryC).
- Измените пространство имен Class1InLibraryB (то есть того, которое не используется в LibracyC), чтобы оно не входило в иерархию пространства имен LibraryC (например, на Ploeh.Samples.LibraryB).
Кажется, что есть некоторая странная взаимосвязь между иерархией пространства имен и соответствием CLS.
Решить эту проблему можно, выбрав один из вариантов в списке выше, но кто-нибудь может объяснить причину такого поведения?
3 ответа
Я посмотрел официальные документы для CLS ( http://msdn.microsoft.com/en-us/netframework/aa569283.aspx), но моя голова взорвалась, прежде чем я смог найти простой ответ.
Но я думаю, что основа состоит в том, что компилятор, чтобы проверить CLS-соответствие LibraryC, должен изучить возможные конфликты имен с LibraryA.
Компилятор должен проверить все "части типа, которые доступны или видимы за пределами определяющей сборки" (правило 1 CLS).
Так как открытый класс Class1InLibraryC наследует Class2InLibraryB, он должен также проверить соответствие CLS для LibraryA, в частности потому, что "Ploeh.*" Теперь находится "в области действия" для правила 5 CLS. "Все имена, введенные в области, совместимой с CLS, должны различаться независимо в своем роде ".
Изменение пространства имен Class1InLibraryB или Class1InLibraryC, чтобы они стали различимыми, похоже, убеждает компилятор, что больше нет шансов для конфликта имен.
Если вы выберете option (2), добавите ссылку и скомпилируете, вы увидите, что ссылка на самом деле не помечена в результирующих метаданных сборки, так что это только зависимость времени компиляции / проверки.
Помните, что 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
папки во время сборки, поэтому они находятся в правильном каталоге при запуске приложения после его компиляции.
Проблема устранена в Roslyn, который доступен в Visual Studio 14.
С июля 2014 года текущая ОСАГО доступна здесь.
Смотрите этот отчет об ошибке для деталей.