29 votes

CLSCompliant(true) entraîne des références inutilisées

Quelqu'un peut-il expliquer le comportement suivant ?

En résumé, si vous créez plusieurs Conforme aux normes CLS dans Visual Studio 2008 et qu'elles partagent un espace de noms commun Root, une bibliothèque référençant une autre bibliothèque sera exiger Les références aux références de cette bibliothèque, même si elles ne les consomment pas.

C'est assez difficile à expliquer en une seule phrase, mais voici les étapes pour reproduire le comportement (faites attention aux espaces de noms) :

Créez une bibliothèque appelée LibraryA et ajoutez-y une classe unique :

namespace Ploeh
{
    public abstract class Class1InLibraryA
    {
    }
}

Assurez-vous que la bibliothèque est conforme à la norme CLS en ajoutant [assembly: CLSCompliant(true)] à AssemblyInfo.cs.

Créez une autre bibliothèque appelée LibraryB et faites référence à LibraryA. Ajoutez les classes suivantes à LibraryB :

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

et

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

Assurez-vous que LibraryB est également conforme aux normes CLS.

Remarquez que Class1InLibraryB dérive d'un type de LibraryA, alors que Class2InLibraryB n'en dérive pas.

Créez maintenant une troisième bibliothèque appelée LibraryC et faites référence à LibraryB (mais pas à LibraryA). Ajoutez la classe suivante :

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

Cela devrait quand même se compiler. Remarquez que Class1InLibraryC dérive de la classe de LibraryB que n'utilise aucun type de la bibliothèque A .

Remarquez également que Class1InLibraryC est défini dans un espace de noms qui fait partie de la hiérarchie d'espaces de noms définie dans LibraryB.

Jusqu'à présent, LibraryC n'a aucune référence à LibraryA, et comme elle n'utilise aucun type de LibraryA, la solution se compile.

Il faut maintenant que LibraryC soit également conforme à CLS. Soudain, la solution ne se compile plus, et le message d'erreur suivant apparaît :

Le type 'Ploeh.Class1InLibraryA' est défini dans un assemblage qui n'est pas référencé. Vous devez ajouter une référence à l'assembly 'Ploeh, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Vous pouvez faire en sorte que la solution soit à nouveau compilée de l'une des manières suivantes :

  • Supprimer la conformité CLS de LibraryC
  • Ajouter une référence à LibraryA (bien que vous n'en ayez pas besoin)
  • Modifier l'espace de noms de LibraryC afin qu'il ne fasse pas partie de la hiérarchie des espaces de noms de LibraryB (par exemple, Fnaah.Samples.LibraryC).
  • Modifier l'espace de noms de Class1InLibraryB (c'est-à-dire l'espace de noms de Class1InLibraryB). no utilisé à partir de LibracyC) de sorte qu'il ne se trouve pas dans la hiérarchie de l'espace de noms de LibraryC (par exemple, vers Ploeh.Samples.LibraryB).

Il semble qu'il y ait une interaction étrange entre la hiérarchie des espaces de noms et la conformité aux CLS.

Il est possible de résoudre ce problème en choisissant l'une des options de la liste ci-dessus. raison à l'origine de ce comportement ?

19voto

user33675 Points 1123

J'ai consulté les documents officiels du CLS ( http://msdn.microsoft.com/en-us/netframework/aa569283.aspx ), mais ma tête a explosé avant que je puisse trouver une réponse simple.

Mais je pense que la base est que le compilateur, afin de vérifier la conformité CLS de LibraryC, doit examiner les éventuels conflits de noms avec LibraryA.

Le compilateur doit vérifier toutes les "parties d'un type qui sont accessibles ou visibles en dehors de l'ensemble de définition" (règle CLS 1).

Étant donné que la classe publique Class1InLibraryC hérite de Class2InLibraryB, elle doit également vérifier la conformité CLS par rapport à LibraryA, notamment parce que "Ploeh.*" est désormais "dans le champ d'application" de la règle CLS 5 "Tous les noms introduits dans un champ d'application conforme aux CLS doivent être distincts, indépendamment de leur nature".

Le fait de modifier l'espace de noms de Class1InLibraryB ou Class1InLibraryC pour qu'ils deviennent distincts semble convaincre le compilateur qu'il n'y a plus de risque de conflit de noms.

Si vous choisissez l'option (2), ajoutez la référence et compilez, vous verrez que la référence n'est pas marquée dans les méta-données de l'assemblage résultant, il s'agit donc d'une dépendance à la compilation/vérification uniquement.

7voto

Scott Dorman Points 25000

N'oubliez pas que le CLS est un ensemble de règles qui s'appliquent aux assemblages générés et qu'il est conçu pour favoriser l'interopérabilité entre les langages. D'une certaine manière, il définit le plus petit sous-ensemble commun de règles qu'un type doit suivre pour garantir qu'il est indépendant du langage et de la plate-forme. La conformité CLS ne s'applique également qu'aux éléments visibles en dehors de l'assemblage qu'ils définissent.

Examen de quelques-unes des lignes directrices que le code conforme aux normes CLS devrait respecter :

  • Évitez d'utiliser des noms couramment utilisés comme mots-clés dans les langages de programmation.
  • Ne pas s'attendre à ce que les utilisateurs du cadre puissent créer des types imbriqués.
  • Supposons que les implémentations de méthodes de même nom et de même signature sur des interfaces différentes soient indépendantes.

Les règles pour déterminer la conformité au CLS sont les suivantes :

  • Lorsqu'un assemblage ne porte pas d'attribut System.CLSCompliantAttribute explicite, il est supposé porter System.CLSCompliananAttribute. est supposé porter l'attribut System.CLSCompliantAttribute(false).
  • Par défaut, un type hérite de l'attribut de conformité CLS de son type englobant (pour les types imbriqués) ou acquiert le niveau de conformité attaché à son assemblage (pour les types de niveau supérieur).
  • Par défaut, les autres membres (méthodes, champs, propriétés et événements) héritent de la conformité CLS de leur type.

En ce qui concerne le compilateur (règle 1 des CLS), il doit être en mesure d'appliquer les règles de conformité aux CLS à toute information qui sera exportée en dehors de l'assemblage et considère qu'un type est conforme aux CLS si toutes ses parties accessibles au public (classes, interfaces, méthodes, champs, propriétés et événements disponibles pour le code s'exécutant dans un autre assemblage) soit

  • ont des signatures composées uniquement de types conformes à CLS, ou
  • sont spécifiquement marqués comme n'étant pas conformes aux normes CLS.

Selon les règles CTS, une portée est simplement un groupe/collection de noms et à l'intérieur d'une portée, un nom peut faire référence à plusieurs entités tant qu'elles sont de nature différente (méthodes, champs, types imbriqués, propriétés, événements) ou qu'elles ont des signatures différentes. Une entité nommée a son nom dans une seule portée, de sorte que pour identifier cette entrée, il faut appliquer à la fois une portée et un nom. La portée qualifie le nom.

Comme les types sont nommés, les noms des types sont également regroupés en champs d'application. Pour identifier complètement un type, le nom du type doit être qualifié par la portée. Les noms de types sont définis par l'assemblage qui contient l'implémentation de ce type.

Pour les champs d'application conformes à CLS, tous les noms doivent être distincts indépendamment de leur nature, sauf si les noms sont identiques et résolus par surcharge. En d'autres termes, alors que la CTS permet à un type unique d'utiliser le même nom pour un champ et une méthode, la CLS ne le permet pas (règle 5 de la CLS).

Pour aller plus loin, un type conforme aux CLS ne doit pas nécessiter la mise en œuvre de types non conformes aux CLS (règle 20 des CLS) et doit également hériter d'un autre type conforme aux CLS (règle 23 des CLS).

Un assemblage peut dépendre d'autres assemblages si les implémentations dans le champ d'application d'un assemblage font référence à des ressources qui sont dans le champ d'application d'un autre assemblage ou qui lui appartiennent.

  • Toutes les références à d'autres assemblages sont résolues sous le contrôle de la portée de l'assemblage actuel.
  • Il est toujours possible de déterminer la portée de l'assemblage dans lequel une implémentation particulière est exécutée. Toutes les requêtes provenant de cette portée d'assemblage sont résolues par rapport à cette portée.

Ce que tout cela signifie en fin de compte, c'est que pour vérifier la conformité d'un type à la norme CLS, le compilateur doit être en mesure de vérifier que tous les parties publiques de ce type sont également conformes aux normes CLS. Cela signifie qu'il doit s'assurer que le nom est unique dans une portée, qu'il ne dépend pas de types non conformes aux CLS pour certaines parties de sa propre implémentation et qu'il hérite d'autres types également conformes aux CLS. La seule façon de le faire est d'examiner tous les assemblages auxquels le type fait référence.

N'oubliez pas que l'étape de construction dans Visual Studio est essentiellement une interface graphique autour de l'exécution de MSBuild, qui n'est finalement rien d'autre qu'un moyen scénarisé d'appeler le compilateur de ligne de commande C#. Pour que le compilateur puisse vérifier la conformité CLS d'un type, il doit connaître et pouvoir trouver tous les assemblages auxquels ce type fait référence (et non le projet). Puisqu'il est appelé par MSBuild et finalement par Visual Studio, la seule façon pour Visual Studio (MSBuild) de l'informer de ces assemblages est de les inclure en tant que références.

Évidemment, puisque le compilateur est capable de déterminer qu'il lui "manque" des références afin de vérifier la conformité aux CLS et de compiler avec succès, il aurait été intéressant qu'il puisse simplement inclure ces références automatiquement en notre nom. Le problème ici est de déterminer qui la version de l'assemblage à inclure et que l'assemblage se trouve sur le système de fichiers. En obligeant le développeur à fournir ces informations, le compilateur contribue à garantir qu'il reçoit des informations correctes. Cela a également pour effet secondaire de s'assurer que tous les assemblages dépendants sont copiés dans le fichier Debug/bin o Release/bin pendant la compilation afin qu'ils se trouvent dans le bon répertoire lorsque l'application est exécutée après avoir été compilée.

1voto

tne Points 1452

Le problème est corrigé dans Roslyn, qui est disponible dans Visual Studio 14.
Depuis juillet 2014, le CTP actuel est disponible aquí .
Voir ce rapport de bogue pour plus de détails.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X