Подтвердить что ты не робот

CLSCompliant (true) перетаскивает неиспользуемые ссылки

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

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

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

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

namespace Ploeh
{
    public abstract class Class1InLibraryA
    {
    }
}

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

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

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 Compliance из LibraryC
  • Добавьте ссылку на LibraryA (хотя вам это не нужно)
  • Измените пространство имен в LibraryC так, чтобы оно не было частью иерархии пространства имен LibraryB (например, Fnaah.Samples.LibraryC)
  • Измените пространство имен Class1InLibraryB (то есть, которое не используется из LibracyC), чтобы оно не лежало в иерархии пространства имен LibraryC (например, Ploeh.Samples.LibraryB)

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

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

4b9b3361

Ответ 1

Я просмотрел официальные документы для 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), добавьте ссылку и скомпилируйте, вы увидите, что ссылка не была отмечена в результирующих метаданных сборки, поэтому это только зависимость от компиляции/проверки времени.

Ответ 2

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

В соответствии с некоторыми рекомендациями CLS-совместимый код должен выглядеть следующим образом:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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