Вопрос

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

Таким образом, если вы создаете несколько 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, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null».

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

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

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

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

Это было полезно?

Решение

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

В конечном итоге все это означает, что для проверки соответствия типа 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 г. доступна текущая CTP-версия. здесь.
Видеть этот отчет об ошибке для получения подробной информации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top