Assemblys de référence

Les assemblys de référence sont un type spécial d’assembly qui contiennent uniquement la quantité minimale de métadonnées requise pour représenter la surface d’API publique de la bibliothèque. Ils incluent des déclarations pour tous les membres qui sont significatifs lors du référencement d’un assembly dans les outils de génération, mais excluent toutes les implémentations de membres et déclarations de membres privés qui n’ont aucun impact observable sur leur contrat d’API. Quant aux assemblys standard, ils sont appelés assemblys d’implémentation.

Les assemblys de référence ne peuvent pas être chargés pour exécution, mais ils peuvent être passés comme entrée du compilateur de la même façon que les assemblys d’implémentation. Les assemblys de référence sont généralement distribués avec le Kit de développement logiciel (SDK) d’une plateforme ou d’une bibliothèque particulière.

En utilisant un assembly de référence, les développeurs peuvent créer des programmes qui ciblent une version de bibliothèque spécifique sans avoir besoin de l’assembly d’implémentation complet pour cette version. Supposons que vous disposez uniquement de la dernière version d’une bibliothèque sur votre ordinateur, mais que vous souhaitez créer un programme qui cible une version antérieure de cette bibliothèque. Si vous compilez directement sur l’assembly d’implémentation, vous risquez d’utiliser par inadvertance des membres d’API qui ne sont pas disponibles dans la version antérieure. Vous ne détecterez cette erreur qu’au moment du test du programme sur l’ordinateur cible. Si vous compilez sur l’assembly de référence pour la version antérieure, vous recevez immédiatement une erreur de compilation.

Un assembly de référence peut également représenter un contrat, c’est-à-dire un ensemble d’API qui ne correspondent pas à l’assembly d’implémentation réel. Ces assemblys de référence, appelés assemblys de contrat, peuvent être utilisés pour cibler plusieurs plateformes qui prennent en charge le même ensemble d’API. Par exemple, .NET Standard fournit l’assembly de contrat netstandard.dll, qui représente l’ensemble d’API communes partagées entre différentes plateformes .NET. Les implémentations de ces API sont contenues dans plusieurs assemblys sur différentes plateformes, comme mscorlib.dll sur .NET Framework ou System.Private.CoreLib.dll sur .NET Core. Une bibliothèque qui cible .NET Standard peut s’exécuter sur toutes les plateformes qui prennent en charge .NET Standard.

Utilisation des assemblys de référence

Pour utiliser certaines API de votre projet, vous devez ajouter des références à leurs assemblys. Vous pouvez ajouter des références à des assemblys d’implémentation ou à des assemblys de référence. Il est toutefois recommandé d’utiliser des assemblys de référence chaque fois qu’ils sont disponibles. Cela garantit que vous utilisez uniquement les membres d’API pris en charge dans la version cible, à destination des concepteurs d’API. L’utilisation de l’assembly de référence garantit que vous ne créez pas de dépendance sur des détails de l’implémentation.

Les assemblys de référence pour les bibliothèques .NET Framework sont distribués avec des packs de ciblage. Vous pouvez les obtenir en téléchargeant un programme d’installation autonome ou en sélectionnant un composant dans le programme d’installation de Visual Studio. Pour plus d'informations, voir Installation de .NET Framework pour les développeurs. Pour .NET Core et .NET Standard, les assemblys de référence sont automatiquement téléchargés au besoin (via NuGet) et référencés. Pour .NET Core 3.0 et les versions ultérieures, les assemblys de référence du framework principal sont fournis dans le package Microsoft.NETCore.App.Ref (ils sont fournis dans le package Microsoft.NETCore.App pour les versions antérieures à la version 3.0).

Lorsque vous ajoutez des références à des assemblys .NET Framework dans Visual Studio à partir de la boîte de dialogue Ajouter une référence, vous sélectionnez un assembly dans la liste, et Visual Studio recherche automatiquement les assemblys de référence qui correspondent à la version de framework cible sélectionnée dans votre projet. Le processus est le même quand vous ajoutez des références directement dans le projet MSBuild en utilisant l’élément de projet Référence : vous avez seulement besoin de spécifier le nom de l’assembly, pas le chemin complet du fichier. Quand vous ajoutez des références à ces assemblys dans la ligne de commande avec l’option -reference du compilateur (en C# et en Visual Basic) ou avec la méthode Compilation.AddReferences dans l’API Roslyn, vous devez spécifier manuellement les fichiers d’assembly de référence pour la version correcte de la plateforme cible. Les fichiers d’assembly de référence .NET Framework se trouvent dans le répertoire %ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETFramework. Pour .NET Core, vous pouvez forcer l’opération de publication à copier les assemblys de référence pour votre plateforme cible dans le sous-répertoire publish/refs de votre répertoire de sortie en définissant la propriété PreserveCompilationContext du projet sur true. Ensuite, vous passez ces fichiers d’assembly de référence au compilateur. L’utilisation de DependencyContext fourni dans le package Microsoft.Extensions.DependencyModel peut faciliter la localisation des chemins des assemblys.

Du fait qu’ils ne contiennent aucune implémentation, les assemblys de référence ne peuvent pas être chargés pour exécution. Toute tentative dans ce sens génère une erreur System.BadImageFormatException. Si vous souhaitez examiner le contenu d’un assembly de référence, vous pouvez le charger dans le contexte de réflexion uniquement dans .NET Framework (avec la méthode Assembly.ReflectionOnlyLoad) ou dans MetadataLoadContext dans .NET et .NET Framework.

Génération des assemblys de référence

La génération d’assemblys de référence pour vos bibliothèques peut être utile lorsque les utilisateurs de ces bibliothèques doivent créer leurs programmes sur de nombreuses versions différentes des bibliothèques. Les assemblys d’implémentation peuvent être difficiles à distribuer en raison de leur grande taille. Les assemblys de référence étant plus petits, leur distribution via le SDK de votre bibliothèque réduit la taille de téléchargement et consomme moins d’espace disque.

Les IDE et les outils de build gagnent également à utiliser des assemblys de référence pour réduire les temps de build dans le cas de solutions volumineuses composées de plusieurs bibliothèques de classes. En règle générale, dans les scénarios de build incrémentielle, un projet est regénéré quand l’un de ses fichiers d’entrée est modifié, y compris les assemblys dont il dépend. L’assembly d’implémentation change chaque fois que le programmeur modifie l’implémentation d’un membre. L’assembly de référence change uniquement lorsque son API publique est impactée. Ainsi, utiliser l’assembly de référence comme fichier d’entrée au lieu de l’assembly d’implémentation permet d’éviter la génération du projet dépendant dans certains cas.

Vous pouvez générer des assemblys de référence :

Si vous souhaitez distribuer des assemblys de référence avec des packages NuGet, vous devez les inclure dans le sous-répertoire ref\ sous le répertoire du package, et non pas dans le sous-répertoire lib\ utilisé pour les assemblys d’implémentation.

Structure des assemblys de référence

Les assemblys de référence sont une extension du concept associé, les assemblys de métadonnées uniquement. Les assemblys de métadonnées uniquement ont leurs corps de méthode remplacés par un corps throw null unique, mais incluent tous les membres à l’exception des types anonymes. L’utilisation de corps throw null (plutôt qu’aucun corps) permet la bonne exécution de PEVerify (et, par voie de conséquence, la validation de la conformité des métadonnées).

En outre, les assemblys de référence suppriment les métadonnées (membres privés) des assemblys de métadonnées uniquement :

  • Un assembly de référence a uniquement des références pour ce dont il a besoin dans la surface de l’API. L’assembly réel peut avoir des références supplémentaires relatives à des implémentations spécifiques. Par exemple, l’assembly de référence pour class C { private void M() { dynamic d = 1; ... } } ne référence aucun des types requis pour dynamic.
  • Les membres de fonction privés (méthodes, propriétés et événements) sont supprimés si leur suppression n’affecte pas la compilation sensiblement. S’il n’y a aucun attribut InternalsVisibleTo, les membres de fonction internes sont également supprimés.

Les métadonnées des assemblys de référence conservent les informations suivantes :

  • Tous les types, y compris les types privés et imbriqués.
  • Tous les attributs, même les attributs internes.
  • Toutes les méthodes virtuelles.
  • Les implémentations d’interfaces explicites.
  • Les propriétés et les événements implémentés explicitement, car leurs accesseurs sont virtuels.
  • Tous les champs de structures.

Les assemblys de référence incluent un attribut ReferenceAssembly de niveau assembly. Cet attribut peut être spécifié dans la source, ce qui évite au compilateur d’avoir à le synthétiser. Quand cet attribut est présent, les runtimes refusent de charger les assemblys de référence pour exécution (mais ces assemblys peuvent être chargés en mode de réflexion uniquement).

Les détails exacts de la structure des assemblys de référence dépendent de la version du compilateur. Les versions plus récentes peuvent choisir d’exclure d’autres métadonnées qui sont déterminées comme n’impactant pas la surface de l’API publique.

Notes

Les informations de cette section s’appliquent uniquement aux assemblys de référence qui sont générés par les compilateurs Roslyn dans C# 7.1 et versions ultérieures ou dans Visual Basic 15.3 et versions ultérieures. La structure des assemblys de référence pour les bibliothèques .NET Framework et .NET Core peut différer sur quelques détails, car elles utilisent leur propre mécanisme de génération d’assemblys de référence. Par exemple, les assemblys peuvent avoir des corps de méthode totalement vides au lieu du corps throw null. Mais le principe général s’applique toujours : ils n’ont pas d’implémentations de méthode utilisables et ils contiennent des métadonnées uniquement pour les membres qui ont un impact observable du point de vue de l’API publique.

Voir aussi