Domanda

Può chiunque spiegare il seguente comportamento?

In sintesi, se crei multiple Conforme al CLS librerie in Visual Studio 2008 e far sì che condividano una radice dello spazio dei nomi comune, lo farà una libreria che fa riferimento a un'altra libreria richiedere riferimenti ai riferimenti di quella libreria anche se non li consuma.

È piuttosto difficile da spiegare in una singola frase, ma ecco i passaggi per riprodurre il comportamento (prestare molta attenzione agli spazi dei nomi):

Crea una libreria chiamata LibraryA e aggiungi una singola classe a quella libreria:

namespace Ploeh
{
    public abstract class Class1InLibraryA
    {
    }
}

Assicurati che la libreria sia conforme a CLS aggiungendo [assembly: CLSCompliant(true)] a AssemblyInfo.cs.

Crea un'altra libreria chiamata LibraryB e fai riferimento a LibraryA.Aggiungi le seguenti classi a LibraryB:

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

E

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

Assicurati che LibraryB sia anche conforme a CLS.

Si noti che Class1InLibraryB deriva da un tipo in LibraryA, mentre Class2InLibraryB no.

Ora crea una terza libreria chiamata LibraryC e fai riferimento a LibraryB (ma non LibraryA).Aggiungi la seguente classe:

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

Questo dovrebbe ancora essere compilato.Si noti che Class1InLibraryC deriva dalla classe in LibraryB that non utilizza alcun tipo da LibraryA.

Si noti inoltre che Class1InLibraryC è definita in uno spazio dei nomi che fa parte della gerarchia dello spazio dei nomi definita in LibraryB.

Finora, LibraryC non ha alcun riferimento a LibraryA e poiché non utilizza tipi di LibraryA, la soluzione viene compilata.

Ora rendi compatibile anche LibraryC CLS.All'improvviso, la soluzione non viene più compilata, dandoti questo messaggio di errore:

Il tipo "Ploeh.Class1InLibraryA" è definito in un assembly a cui non viene fatto riferimento.È necessario aggiungere un riferimento all'assembly "Ploeh, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null".

È possibile compilare nuovamente la soluzione in uno dei seguenti modi:

  • Rimuovere la conformità CLS da LibraryC
  • Aggiungi un riferimento a LibraryA (anche se non ti serve)
  • Modificare lo spazio dei nomi in LibraryC in modo che non faccia parte della gerarchia dello spazio dei nomi di LibraryB (ad es.a Fnaah.Samples.LibraryC)
  • Modificare lo spazio dei nomi di Class1InLibraryB (ovvero quello non utilizzato da LibracyC) in modo che non si trovi nella gerarchia dello spazio dei nomi di LibraryC (ad es.a Ploeh.Samples.LibraryB)

Sembra che ci sia una strana interazione tra la gerarchia dello spazio dei nomi e la conformità CLS.

È possibile risolvere questo problema selezionando una delle opzioni nell'elenco sopra, ma qualcuno può spiegarlo motivo dietro questo comportamento?

È stato utile?

Soluzione

Ho dato un'occhiata nei documenti ufficiali per la CLS ( http: // msdn.microsoft.com/en-us/netframework/aa569283.aspx ), ma la mia testa è esplosa prima che potessi trovare una risposta semplice.

Ma penso che la base è che il compilatore, al fine di verificare la conformità CLS di LibraryC, ha bisogno di esaminare possibili conflitti di denominazione con LibraryA.

Il compilatore deve verificare tutte le "parti di un tipo che non rientrano accessibili o visibili del complesso definire" (CLS regola 1).

Dal public class Class1InLibraryC eredita Class2InLibraryB, deve verificare il rispetto CLS contro LibraryA pure, in particolare perché "Ploeh. *" È ora "di portata" per CLS Regola 5 "Tutti i nomi introdotti in un ambito CLS-compliant deve essere indipendente genere distinto".

La modifica sia lo spazio dei nomi di Class1InLibraryB o Class1InLibraryC così diventano distinti sembra convincere il compilatore non v'è alcuna possibilità di un conflitto di nomi più.

Se si sceglie l'opzione (2), aggiungere il riferimento e compilare, vedrete che il riferimento non è in realtà segnato nel conseguente assemblaggio meta-dati, quindi questo è solo una dipendenza di compilazione / verifica in tempo.

Altri suggerimenti

Ricordare che CLS è un insieme di regole che si applicano agli assembly generati ed è progettato per supportare l'interoperabilità tra linguaggi.In un certo senso, definisce il più piccolo sottoinsieme comune di regole che un tipo deve seguire per garantire che sia indipendente dal linguaggio e dalla piattaforma.La conformità CLS si applica anche solo agli elementi visibili al di fuori del loro assieme definito.

Osservando alcune delle linee guida che il codice conforme a CLS dovrebbe seguire:

  • Evitare l'uso di nomi comunemente usati come parole chiave nei linguaggi di programmazione.
  • Non aspettarsi che gli utenti del framework siano in grado di creare tipi annidati.
  • Supponiamo che le implementazioni di metodi con lo stesso nome e firma su interfacce diverse siano indipendenti.

Le regole per determinare la conformità CLS sono:

  • Quando un assieme non trasporta un sistema esplicito.
  • Per impostazione predefinita, un tipo eredita l'attributo di conformità CLS del tipo che lo racchiude (per i tipi annidati) o acquisisce il livello di conformità associato al relativo assembly (per i tipi di livello superiore).
  • Per impostazione predefinita, gli altri membri (metodi, campi, proprietà ed eventi) ereditano la conformità CLS del loro tipo.

Ora, per quanto riguarda il compilatore, (regola 1 di CLS) deve essere in grado di applicare le regole per la conformità a CLS a qualsiasi informazione che verrà esportata all'esterno dell'assembly e considera un tipo conforme a CLS se tutto è pubblico parti accessibili (quelle classi, interfacce, metodi, campi, proprietà ed eventi disponibili per l'esecuzione del codice in un altro assembly)

  • avere firme composte solo da tipi conformi a CLS oppure
  • sono specificatamente contrassegnati come non conformi a CLS.

Secondo le regole CTS, un ambito è semplicemente un gruppo/raccolta di nomi e all'interno di un ambito un nome può fare riferimento a più entità purché siano di tipo diverso (metodi, campi, tipi nidificati, proprietà, eventi) o abbiano firme diverse.Un'entità denominata ha il suo nome esattamente in un ambito, quindi per identificare quella voce è necessario applicare sia un ambito che un nome.L'ambito qualifica il nome.

Poiché i tipi hanno un nome, anche i nomi dei tipi vengono raggruppati in ambiti.Per identificare completamente un tipo, il nome del tipo deve essere qualificato dall'ambito.I nomi dei tipi rientrano nell'ambito dell'assembly che contiene l'implementazione di quel tipo.

Per gli ambiti conformi a CLS, tutti i nomi devono essere distinti indipendentemente dal tipo, tranne i casi in cui i nomi sono identici e risolti tramite sovraccarico.In altre parole, mentre il CTS consente a un singolo tipo di utilizzare lo stesso nome per un campo e un metodo, il CLS no (Regola CLS 5).

Facendo un ulteriore passo avanti, un tipo conforme a CLS non deve richiedere l'implementazione di tipi non conformi a CLS (regola 20 CLS) e deve anche ereditare da un altro tipo di reclamo CLS (regola 23 CLS).

Un assembly può dipendere da altri assembly se le implementazioni nell'ambito di un assembly fanno riferimento a risorse incluse o di proprietà di un altro assembly.

  • Tutti i riferimenti ad altri assembly vengono risolti sotto il controllo dell'ambito dell'assembly corrente.
  • È sempre possibile determinare in quale ambito dell'assembly è in esecuzione una particolare implementazione.Tutte le richieste originate da quell'ambito dell'assembly vengono risolte rispetto a quell'ambito.

Ciò che tutto ciò significa in definitiva è che per verificare la conformità CLS di un tipo, il compilatore deve essere in grado di verificarlo Tutto anche le parti pubbliche di quel tipo sono conformi a CLS.Ciò significa che deve garantire che il nome sia univoco all'interno di un ambito, che non dipenda da tipi non conformi a CLS per parti della propria implementazione e che erediti da altri tipi anch'essi conformi a CLS.L'unico modo per farlo è esaminare tutti gli assembly a cui fa riferimento il tipo.

Ricordare che il passaggio di compilazione in Visual Studio è essenzialmente un wrapper GUI attorno all'esecuzione di MSBuild, che in definitiva non è altro che un modo basato su script per chiamare il compilatore della riga di comando C#.Affinché il compilatore possa verificare la conformità CLS di un tipo, deve conoscere ed essere in grado di trovare tutti gli assembly a cui fa riferimento il tipo (non il progetto).Poiché viene chiamato tramite MSBuild e, in definitiva, Visual Studio, l'unico modo per Visual Studio (MSBuild) di informarlo di tali assembly è includerli come riferimenti.

Ovviamente, poiché il compilatore è in grado di capire che ci sono riferimenti "mancanti" per verificare la conformità CLS e compilare con successo, sarebbe stato carino se avesse potuto semplicemente includere automaticamente tali riferimenti per nostro conto.Il problema qui è nel determinare Quale versione dell'assembly da includere e Dove quell'assembly si trova sul file system.Obbligando lo sviluppatore a fornire tali informazioni, il compilatore aiuta a garantire che vengano fornite le informazioni corrette.Ciò ha anche l'effetto collaterale di garantire che tutti gli assembly dipendenti vengano copiati nel file Debug/bin O Release/bin cartelle durante la compilazione in modo che si trovino nella directory corretta quando l'applicazione viene eseguita dopo essere stata compilata.

Il problema è risolto in Roslyn, che è disponibile in Visual Studio 14.
A partire da luglio 2014, il CTP corrente è disponibile qui .
Vedere questo bug report per i dettagli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top