Assembly di riferimento

Gli assembly di riferimento sono un tipo speciale di assembly che contiene solo la quantità minima di metadati necessari per rappresentare la superficie API pubblica della libreria. Includono dichiarazioni per tutti i membri significativi per fare riferimento a un assembly negli strumenti di compilazione, ma escludono tutte le implementazioni dei membri e le dichiarazioni dei membri privati che non hanno alcun impatto osservabile sul contratto API. Al contrario, gli assembly regolari vengono chiamati assembly di implementazione.

Gli assembly di riferimento non possono essere caricati per l'esecuzione, ma possono essere passati come input del compilatore nello stesso modo degli assembly di implementazione. Gli assembly di riferimento vengono in genere distribuiti con Software Development Kit (SDK) di una determinata piattaforma o libreria.

L'uso di un assembly di riferimento consente agli sviluppatori di compilare programmi destinati a una versione specifica della libreria senza disporre dell'assembly di implementazione completo per tale versione. Si supponga di avere solo la versione più recente di alcune librerie nel computer, ma si vuole creare un programma destinato a una versione precedente di tale libreria. Se si esegue la compilazione direttamente nell'assembly di implementazione, è possibile usare inavvertitamente membri API non disponibili nella versione precedente. Questo errore si verificherà solo durante il test del programma nel computer di destinazione. Se si esegue la compilazione in base all'assembly di riferimento per la versione precedente, si otterrà immediatamente un errore in fase di compilazione.

Un assembly di riferimento può anche rappresentare un contratto, ovvero un set di API che non corrispondono all'assembly di implementazione concreto. Tali assembly di riferimento, denominati assembly del contratto, possono essere usati per usare più piattaforme che supportano lo stesso set di API. Ad esempio, .NET Standard fornisce l'assembly del contratto, netstandard.dll, che rappresenta il set di API comuni condivise tra diverse piattaforme .NET. Le implementazioni di queste API sono contenute in assembly diversi in piattaforme diverse, ad esempio mscorlib.dll in .NET Framework o System.Private.CoreLib.dll in .NET Core. Una libreria destinata a .NET Standard può essere eseguita in tutte le piattaforme che supportano .NET Standard.

Uso di assembly di riferimento

Per usare determinate API del progetto, è necessario aggiungere riferimenti agli assembly. È possibile aggiungere riferimenti agli assembly di implementazione o agli assembly di riferimento. È consigliabile usare assembly di riferimento ogni volta che sono disponibili. In questo modo si garantisce di usare solo i membri api supportati nella versione di destinazione, destinati a essere usati dalle finestre di progettazione API. L'uso dell'assembly di riferimento garantisce che non si stia prendendo una dipendenza dai dettagli di implementazione.

Gli assembly di riferimento per le librerie .NET Framework vengono distribuiti con i pacchetti di destinazione. È possibile ottenerli scaricando un programma di installazione autonomo o selezionando un componente nel programma di installazione di Visual Studio. Per altre informazioni, vedere Installare .NET Framework per sviluppatori. Per .NET Core e .NET Standard, gli assembly di riferimento vengono scaricati automaticamente in base alle esigenze (tramite NuGet) e a cui si fa riferimento. Per .NET Core 3.0 e versioni successive, gli assembly di riferimento per il framework principale si trovano nel pacchetto Microsoft.NETCore.App.Ref (il pacchetto Microsoft.NETCore.App viene usato invece per le versioni precedenti alla 3.0).

Quando si aggiungono riferimenti agli assembly .NET Framework in Visual Studio usando la finestra di dialogo Aggiungi riferimento , si seleziona un assembly dall'elenco e Visual Studio trova automaticamente assembly di riferimento che corrispondono alla versione del framework di destinazione selezionata nel progetto. Lo stesso vale per l'aggiunta di riferimenti direttamente nel progetto MSBuild usando l'elemento progetto riferimento : è sufficiente specificare il nome dell'assembly, non il percorso completo del file. Quando si aggiungono riferimenti a questi assembly nella riga di comando usando l'opzione del -reference compilatore (in C# e in Visual Basic) o usando il Compilation.AddReferences metodo nell'API Roslyn, è necessario specificare manualmente i file di assembly di riferimento per la versione corretta della piattaforma di destinazione. I file di assembly di riferimento di .NET Framework si trovano in %ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\. Directory NETFramework . Per .NET Core, è possibile forzare l'operazione di pubblicazione per copiare gli assembly di riferimento per la piattaforma di destinazione nella sottodirectory publish/refs della directory di output impostando la PreserveCompilationContext proprietà del progetto su true. È quindi possibile passare questi file di assembly di riferimento al compilatore. L'uso del DependencyContextpacchetto Microsoft.Extensions.DependencyModel consente di individuare i percorsi.

Poiché non contengono implementazioni, gli assembly di riferimento non possono essere caricati per l'esecuzione. Se si tenta di eseguire questa operazione, viene restituito un oggetto System.BadImageFormatException. Per esaminare il contenuto di un assembly di riferimento, è possibile caricarlo nel contesto di sola reflection in .NET Framework (usando il Assembly.ReflectionOnlyLoad metodo ) o in MetadataLoadContext in .NET e .NET Framework.

Generazione di assembly di riferimento

La generazione di assembly di riferimento per le librerie può essere utile quando gli utenti della libreria devono compilare i programmi in molte versioni diverse della libreria. La distribuzione di assembly di implementazione per tutte queste versioni potrebbe risultare poco pratica a causa delle dimensioni elevate. Gli assembly di riferimento sono più piccoli e distribuirli come parte dell'SDK della libreria riduce le dimensioni del download e risparmia spazio su disco.

Gli IDE e gli strumenti di compilazione possono anche sfruttare i vantaggi degli assembly di riferimento per ridurre i tempi di compilazione in caso di soluzioni di grandi dimensioni costituite da più librerie di classi. In genere, negli scenari di compilazione incrementale un progetto viene ricompilato quando uno dei relativi file di input viene modificato, inclusi gli assembly da cui dipende. L'assembly di implementazione cambia ogni volta che il programmatore modifica l'implementazione di qualsiasi membro. L'assembly di riferimento cambia solo quando è interessata l'API pubblica. Pertanto, l'uso dell'assembly di riferimento come file di input invece dell'assembly di implementazione consente di ignorare la compilazione del progetto dipendente in alcuni casi.

È possibile generare assembly di riferimento:

Se si desidera distribuire assembly di riferimento con pacchetti NuGet, è necessario includerli nella sottodirectory ref\ nella directory del pacchetto anziché nella sottodirectory lib\ usata per gli assembly di implementazione.

Struttura degli assembly di riferimento

Gli assembly di riferimento sono un'espansione del concetto correlato, ovvero gli assembly solo metadati. Per gli assembly di soli metadati i corpi di metodo vengono sostituiti con un singolo corpo throw null, ma sono inclusi tutti i membri, tranne i tipi anonimi. Il motivo dell'uso throw null di corpi (anziché di nessun corpo) è in modo che PEVerify possa essere eseguito e passato (convalidando così la completezza dei metadati).

Gli assembly di riferimento rimuovono ulteriormente i metadati (membri privati) dagli assembly di soli metadati:

  • Un assembly di riferimento include solo i riferimenti per i requisiti nella superficie dell'API. L'assembly reale può includere riferimenti aggiuntivi relativi a implementazioni specifiche. Ad esempio, l'assembly di riferimento per class C { private void M() { dynamic d = 1; ... } } non fa riferimento ad alcun tipo necessario per dynamic.
  • I membri-funzione privati (metodi, proprietà ed eventi) vengono rimossi quando la rimozione non impatta in maniera visibile sulla compilazione. Se non sono presenti attributi InternalsVisibleTo , vengono rimossi anche i membri della funzione interna.

I metadati negli assembly di riferimento continuano a mantenere le informazioni seguenti:

  • Tutti i tipi, inclusi i tipi privati e annidati.
  • Tutti gli attributi, anche quelli interni.
  • Tutti i metodi virtuali.
  • Implementazioni esplicite dell'interfaccia.
  • Le proprietà e gli eventi implementati in modo esplicito, perché le funzioni di accesso sono virtuali.
  • Tutti i campi delle strutture.

Gli assembly di riferimento includono un attributo ReferenceAssembly a livello di assembly. Questo attributo può essere specificato nell'origine; quindi il compilatore non dovrà sintetizzarlo. A causa di questo attributo, i runtime rifiutano di caricare assembly di riferimento per l'esecuzione (ma possono essere caricati in modalità di sola reflection).

I dettagli della struttura dell'assembly di riferimento esatto dipendono dalla versione del compilatore. Le versioni più recenti possono scegliere di escludere più metadati se sono determinati come non influiscono sulla superficie dell'API pubblica.

Nota

Le informazioni contenute in questa sezione sono applicabili solo agli assembly di riferimento generati dai compilatori Roslyn a partire da C# versione 7.1 o Visual Basic versione 15.3. La struttura degli assembly di riferimento per le librerie .NET Framework e .NET Core può differire in alcuni dettagli, perché usano il proprio meccanismo di generazione di assembly di riferimento. Ad esempio, potrebbero avere corpi di metodo completamente vuoti anziché il throw null corpo. Ma il principio generale si applica ancora: non hanno implementazioni di metodi utilizzabili e contengono metadati solo per i membri che hanno un impatto osservabile dal punto di vista dell'API pubblica.

Vedi anche