2009-08-10 4 views
29

Может ли кто-нибудь объяснить следующее поведение?CLSCompliant (true) перетаскивает неиспользованные ссылки

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

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

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

namespace Ploeh 
{ 
    public abstract class Class1InLibraryA 
    { 
    } 
} 

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

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

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

и

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

Убедитесь, что также библиотека B CLS Compliant.

Обратите внимание, что Class1InLibraryB происходит от типа библиотеки, тогда как Class2InLibraryB этого не делает.

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

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

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

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

До сих пор LibraryC не имеет ссылки на LibraryA, и поскольку он не использует никаких типов из библиотекиA, решение компилируется.

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

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

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

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

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

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

+0

+1 Я тоже это заметил, но не успел открыть вопрос. – Jehof

+0

Очень интересно. Я никогда не сталкивался с этим, потому что я никогда не использовал одно и то же пространство имен в двух проектах библиотеки классов. – RichardOD

+0

Ударьте его тоже, и [открыл вопрос] (http://stackoverflow.com/q/24700730) независимо, прежде чем, наконец, обнаружив это. Я [подал ошибку] ​​(https://connect.microsoft.com/VisualStudio/feedback/details/920175/cls-compliance-and-build-time-transitive-dependency-requirements), как было предложено в другом вопросе. Если у вас есть какие-либо новые материалы по этому вопросу, мне тоже будет интересно. – tne

ответ

19

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

+0

Вероятно, вы на что-то, но я до сих пор этого не понимаю. Почему изменение пространства имен Class1InLibraryB в другое пространство под-имен «гарантирует», что конфликт имен не будет? Даже если полное имя типа было Ploeh.Samples.LibraryB.Class1InLibraryB, я все же мог определить тип в LibraryA под названием Ploeh.Samples.LibraryC.Class1InLibraryC ... –

+0

Вы правы, это мягкое пятно в моей аргументации. Поведение связано с анализом пространства имен, который должен выполнить компилятор, чтобы найти Class1InLibraryA и убедиться, что он не нарушает соответствие CLS. Неопределенность пространства имен, возможно, специфична для C# (и Visual Basic.NET), поскольку на языках, указывающих сборку, в ссылке статического типа нет средств. Тем не менее, я думаю, из-за этого компиляторы берут ярлыки пространства имен, чтобы проверить правило CLS 5. Дело в том, что пространства имен являются языковой функцией, а не требованиями CLS. – user33675

+0

@henbo: Спасибо за ваше объяснение. Я никогда не думал о том, что пространства имен являются языковой функцией. Я все еще не уверен, что получаю это, но мне нужно немного подумать, я думаю ... –

7

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

Глядя на некоторые из руководящих принципов CLS-совместимый код должен следовать:

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

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

  • Когда сборка не несет явную System.CLSCompliantAttribute, оно должно быть предполагается носить System.CLSCompliantAttribute (ложь).
  • По умолчанию тип наследует атрибут 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, которая в конечном счете является не чем иным, как сценарием для вызова компилятора командной строки C#. Чтобы компилятор мог проверить CLS-соответствие типа, он должен знать и иметь возможность находить все сборки, которые ссылаются на типы (а не проект). Поскольку он вызывается через MSBuild и, в конечном счете, Visual Studio, единственный способ для Visual Studio (MSBuild) информировать его об этих сборках - это включить их в качестве ссылок.

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

+0

Да , но * no * в ссылочных типах LibraryC в LibraryA, поэтому я до сих пор не понимаю, зачем нужна ссылка на LibraryA. –

+0

Да, но LibraryC ссылается на библиотеку B, которая ссылается на LibraryA ... поэтому для того, чтобы компилятор мог понять, что что-то в LibraryB действительно соответствует CLS, он должен также опросить LibraryA, и для этого он должен быть включен как ссылка. –

+0

Помните, что вы отмечаете всю сборку как CLS-совместимую, а не только определенные типы. Поскольку вся сборка отмечена как CLS-совместимая, компилятор должен проверять все общедоступные типы. –

1

Вопрос исправлен в Roslyn, который доступен в Visual Studio 14.
Начиная с июля 2014 года существует действующий CTP here.
Подробнее см. this bug report.

 Смежные вопросы

  • Нет связанных вопросов^_^