CLSCompliant(true)拖拽未使用的引用

Mar*_*ann 29 .net namespaces reference cls-compliant visual-studio-2008

任何人都可以解释以下行为吗?

总之,如果在Visual Studio 2008中创建多个符合CLS的库并使它们共享一个公共命名空间根,则引用另一个库的库将需要引用该库的引用,即使它不使用它们.

用一句话解释起来相当困难,但这里是重现行为的步骤(密切关注命名空间):

创建一个名为LibraryA的库,并向该库添加一个单独的类:

namespace Ploeh
{
    public abstract class Class1InLibraryA
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

通过添加[assembly: CLSCompliant(true)]到AssemblyInfo.cs 确保库是CLS Compliant .

创建另一个名为LibraryB的库并引用LibraryA.将以下类添加到LibraryB:

namespace Ploeh.Samples
{
    public class Class1InLibraryB : Class1InLibraryA
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

namespace Ploeh.Samples
{
    public abstract class Class2InLibraryB
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

确保LibraryB也符合CLS.

请注意,Class1InLibraryB派生自LibraryA中的类型,而Class2InLibraryB则不派生.

现在创建一个名为LibraryC的第三个库,并引用LibraryB(但不是LibraryA).添加以下类:

namespace Ploeh.Samples.LibraryC
{
    public class Class1InLibraryC : Class2InLibraryB
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该仍然可以编译.请注意,Class1InLibraryC派生自LibraryB 中不使用LibraryA中任何类型的类.

另请注意,Class1InLibraryC是在名称空间中定义的,该名称空间是LibraryB中定义的名称空间层次结构的一部分.

到目前为止,LibraryC没有引用LibraryA,因为它不使用LibraryA中的任何类型,所以解决方案会编译.

现在也使LibraryC CLS兼容.突然,解决方案不再编译,给你这个错误信息:

类型'Ploeh.Class1InLibraryA'在未引用的程序集中定义.您必须添加对程序集'Ploeh,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'的引用.

您可以通过以下方式之一再次编译解决方案:

  • 从LibraryC中删除CLS合规性
  • 添加对LibraryA的引用(尽管您不需要它)
  • 更改LibraryC中的命名空间,使其不是LibraryB的命名空间层次结构的一部分(例如,到Fnaah.Samples.LibraryC)
  • 更改Class1InLibraryB的名称空间(即使用LibracyC的名称空间),以便它不在LibraryC的名称空间层次结构中(例如,到Ploeh.Samples.LibraryB)

似乎命名空间层次结构和CLS合规性之间存在一些奇怪的相互作用.

解决这个问题可以通过选择上面列表中的一个选项来完成,但任何人都可以解释这种行为背后的原因吗?

use*_*675 19

我查看了CLS的官方文档(http://msdn.microsoft.com/en-us/netframework/aa569283.aspx),但在我找到一个简单的答案之前,我的头部爆炸了.

但我认为基础是编译器为了验证LibraryC的CLS合规性,需要研究与LibraryA的可能的命名冲突.

编译器必须验证所有"在定义程序集之外可访问或可见的类型的部分"(CLS规则1).

由于公共类Class1InLibraryC继承Class2InLibraryB,因此它必须验证对LibraryA的CLS合规性,特别是因为"Ploeh.*"现在是"在范围内"的CLS规则5"在符合CLS的范围中引入的所有名称应是独立的善良的".

更改Class1InLibraryB或Class1InLibraryC的命名空间使它们变得不同似乎说服编译器不再存在名称冲突的可能性.

如果选择选项(2),添加引用和编译,您将看到引用实际上没有标记在生成的程序集元数据中,因此这只是一个编译/验证时依赖项.


Sco*_*man 7

请记住,CLS是一组适用于生成的程序集的规则,旨在支持语言之间的互操作性.从某种意义上说,它定义了类型必须遵循的最小公共规则子集,以确保它与语言和平台无关.CLS合规性也仅适用于在其定义程序集外部可见的项目.

查看符合CLS的代码应遵循的一些准则:

  • 避免在编程语言中使用常用作关键字的名称.
  • 不要指望框架的用户能够创作嵌套类型.
  • 假设在不同接口上具有相同名称和签名的方法的实现是独立的.

确定CLS合规性的规则是:

  • 当程序集不带有显式的System.CLSCompliantAttribute时,应假定它携带System.CLSCompliantAttribute(false).
  • 默认情况下,类型继承其封闭类型的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中的构建步骤本质上是围绕执行MSBuild的GUI包装器,它最终只是一种调用C#命令行编译器的脚本方式.为了使编译器验证类型的CLS兼容性,它必须知道并能够找到键入引用(而不是项目)的所有程序集.由于它是通过MSBuild和最终的Visual Studio调用的,因此Visual Studio(MSBuild)通知它们这些程序集的唯一方法是将它们作为引用包含在内.

显然,由于编译器能够确定它是"缺少"引用以便验证CLS兼容性并成功编译,如果它可以代表我们自动包含那些引用,那将是很好的.这里的问题是决定和版本的程序集,包括其中该程序集是在文件系统上.通过强制开发人员提供该信息,编译器可以帮助确保为其提供正确的信息.这还有一个副作用,即确保在构建期间将所有依赖程序集复制到Debug/binRelease/bin文件夹,以便在编译应用程序后运行它们时它们位于正确的目录中.