Pergunta

Alguém pode explicar o seguinte comportamento?

Em resumo, se você criar múltiplos compatível com CLS bibliotecas em Visual Studio 2008 e tê-los compartilhar uma raiz namespace comum, uma biblioteca de referência outra biblioteca requerem as referências a esse referências da biblioteca, mesmo que ele não consumi-los.

É muito difícil de explicar em uma única frase, mas aqui estão os passos para reproduzir o comportamento (pay muita atenção para os espaços de nomes):

Criar uma biblioteca chamada LibraryA e adicionar uma classe única para essa biblioteca:

namespace Ploeh
{
    public abstract class Class1InLibraryA
    {
    }
}

Certifique-se de que a biblioteca é compatível com CLS, adicionando [assembly: CLSCompliant(true)] para AssemblyInfo.cs.

Crie outra biblioteca chamada LibraryB e referência LibraryA. Adicione as seguintes classes para LibraryB:

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

e

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

Certifique-se de que LibraryB também é compatível com CLS.

Observe que deriva Class1InLibraryB de um tipo em LibraryA, enquanto Class2InLibraryB não.

Agora crie uma terceira biblioteca chamada LibraryC e referência LibraryB (mas não LibraryA). Adicionar a seguinte classe:

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

Isso ainda deve compilar. Observe que deriva Class1InLibraryC da classe em LibraryB que não usa qualquer tipo de LibraryA .

Note também que Class1InLibraryC é definida em um espaço que é parte da hierarquia de namespace definido no LibraryB.

Até agora, LibraryC não tem referência para LibraryA, e uma vez que utiliza nenhum tipo de LibraryA, as compilações de solução.

Agora faça compatível LibraryC CLS também. De repente, compila a solução não mais, dando-lhe esta mensagem de erro:

O tipo 'Ploeh.Class1InLibraryA' é definido em um conjunto que não é referenciado. Você deve adicionar uma referência ao assembly 'Ploeh, versão = 1.0.0.0, Culture = neutral, PublicKeyToken = null'.

Você pode fazer a compilação solução novamente em uma das seguintes formas:

  • Remover CLS Compliance da LibraryC
  • Adicione uma referência para LibraryA (embora você não precisa dele)
  • Altere o namespace em LibraryC para que ele não faz parte da hierarquia de namespace do LibraryB (por exemplo, para Fnaah.Samples.LibraryC)
  • Altere o namespace de Class1InLibraryB (isto é, a um não usado a partir LibracyC) para que ele seja não reside na hierarquia de namespace do LibraryC (por exemplo, para Ploeh.Samples.LibraryB)

Parece que há alguma estranha interação entre a hierarquia namespace e compatibilidade com CLS.

Resolver este problema pode ser feito escolhendo uma das opções na lista acima, mas alguém pode explicar o razão por trás deste comportamento?

Foi útil?

Solução

Eu tinha um olhar para os documentos oficiais para o CLS ( http: // msdn.microsoft.com/en-us/netframework/aa569283.aspx ), mas a minha cabeça explodiu antes que eu pudesse encontrar uma resposta simples.

Mas eu acho que a base é que o compilador, a fim de verificar o cumprimento CLS de LibraryC, precisa de olhar para possíveis conflitos de nomeação com LibraryA.

O compilador deve verificar todas as "partes de um tipo que estão fora acessível ou visível da definição de montagem" (CLS Regra 1).

Desde classe pública Class1InLibraryC herda Class2InLibraryB, deve verificar o cumprimento CLS contra LibraryA, bem como, em particular porque "Ploeh. *" É agora "no escopo" para CLS Regra 5 "Todos os nomes introduzidos em um escopo compatível com CLS deve ser independente distinta de tipo".

A alteração ou o namespace de Class1InLibraryB ou Class1InLibraryC para que se tornem distintos parece convencer o compilador não há chance para um conflito de nome mais.

Se você escolher a opção (2), adicionar a referência e de compilação, você verá que a referência não é realmente marcado na Assembleia meta-dados resultantes, por isso esta é apenas uma compilação / verificação em tempo de dependência.

Outras dicas

Lembre-se que os CLS é um conjunto de regras que se aplicam aos conjuntos gerados e é projetado para suportar a interoperabilidade entre linguagens. Em certo sentido, ela define o menor subconjunto comum de regras que um tipo deve seguir para garantir que é linguagem e agnóstico plataforma. CLS-conformidade também se aplica apenas aos itens que são visíveis fora de sua definição de montagem.

Olhando para alguns do código diretrizes compatível com CLS deve seguir:

  • Evite o uso de nomes comumente usados ??como palavras-chave em linguagens de programação.
  • Não espere que os usuários do quadro para ser capaz de autor tipos aninhados.
  • Suponha que implementações de métodos do mesmo nome e assinatura em diferentes interfaces são independentes.

As regras para determinar CLS-conformidade são:

  • Quando um assembly não carrega um System.CLSCompliantAttribute explícita, será assumido de levar System.CLSCompliantAttribute (false).
  • por padrão, um tipo herda o atributo CLS-cumprimento do seu tipo envolvente (em forma de tipos aninhados) ou adquire o grau de adesão ligados à sua montagem (para os tipos de alto nível).
  • Por padrão, outros membros (métodos, campos, propriedades e eventos) herdar o CLS cumprimento do seu tipo.

Agora, tanto quanto o compilador está em causa, (CLS Regra 1) ele deve ser capaz de aplicar as regras para CLS-cumprimento a qualquer informação que serão exportados para fora do conjunto e considera um tipo para ser compatível com CLS se todas as suas partes acessíveis ao público (essas classes, interfaces, métodos, campos, propriedades e eventos que estão disponíveis para código em execução em outro assembly) seja

  • ter assinaturas composto apenas de tipos compatíveis com CLS, ou
  • são especificamente marcado como não compatível com CLS.

Por regras CTS, um escopo é simplesmente um grupo / coleção de nomes e dentro de um escopo de um nome pode se referir a várias entidades, desde que eles são de diferentes tipos (métodos, campos, tipos aninhados, propriedades, eventos) ou ter assinaturas diferentes. A entidade nomeada tem o seu nome em exatamente um âmbito de modo a fim de identificar que a entrada tanto um âmbito e um nome deve ser aplicada. O escopo qualifica o nome.

Uma vez que os tipos são nomeados, os nomes de tipos são também agrupados no âmbitos. Para identificar completamente um tipo, o nome do tipo deve ser qualificado pelo escopo. nomes de tipos são escopo do assembly que contém a implementação desse tipo.

Para escopos que são compatíveis com CLS, todos os nomes devem ser independentes distinta da espécie, excepto quando os nomes são idênticos e resolveu através de sobrecarga. Em outras palavras, enquanto o CTS permite que um único tipo de usar o mesmo nome para um campo e um método, os CLS não (CLS Regra 5).

Tomar ainda mais este passo, um tipo compatível com CLS não deve exigir a implementação de não-compatíveis com CLS tipos (CLS Regra 20) e também deve herdar de outro tipo CLS-denúncia (CLS Regra 23).

Uma montagem pode depender de outros conjuntos se as implementações no âmbito dos recursos de referência uma montagem que são escopo em ou pertencentes a outra montagem.

  • Todas as referências a outros conjuntos são resolvidos sob o controlo do alcance de montagem corrente.
  • É sempre possível determinar qual o âmbito de montagem uma implementação específica está sendo executado. Todos os pedidos provenientes desse escopo montagem são resolvidos em relação a esse escopo.

O que tudo isto significa em última análise é que, a fim de verificar-CLS cumprimento de um tipo, o compilador deve ser capaz de verificar que todas Peças públicas desse tipo são também compatíveis com CLS. Isso significa que ele deve garantir que o nome é exclusivo dentro de um escopo, que não depende de tipos não-compatível com CLS para as peças de sua própria implementação e que herda de outros tipos que também são compatíveis com CLS. A única maneira para que possa fazê-lo é através da análise de todos os assemblies que o tipo de referências.

Lembre-se que o passo de compilação em Visual Studio é essencialmente uma GUI wrapper para executar MSBuild, que em última análise, nada mais é do que uma maneira de script para chamar o C # linha de comando do compilador. Para que o compilador para verificar CLS-conformidade de um tipo, ele deve saber de e ser capaz de encontrar todas as montagens que referências de tipo (não o projeto). Uma vez que é chamado através MSBuild e, finalmente, Visual Studio, a única maneira para Visual Studio (MSBuild) para informá-lo dessas montagens é, incluindo-os como referências.

Obviamente, uma vez que o compilador é capaz de descobrir o que está "faltando" referências, a fim de verificar CLS-conformidade e compilar com sucesso, ele teria sido bom se ele poderia simplesmente ter incluído os de referência automaticamente em nosso nome. O problema aqui é na determinação que Versão do conjunto de incluir e , onde que a montagem está no sistema de arquivos. Ao forçar o desenvolvedor para fornecer essa informação, o compilador ajuda a garantir que é dada a informação correta. Isto também tem o efeito colateral de assegurar que todos os conjuntos dependentes são copiados para as pastas Debug/bin ou Release/bin durante a construção para que eles estão no diretório correto quando a aplicação é executada depois de ter sido compilado.

O problema é corrigido no Roslyn, que está disponível no Visual Studio 14.
A partir de julho de 2014, a corrente de CTP é aqui .
Consulte este relatório de erro para obter mais informações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top