Inoltro dei tipi in Common Language Runtime

L'inoltro dei tipi consente di spostare un tipo in un altro assembly senza dover ricompilare le applicazioni in cui viene utilizzato l'assembly originale.

Si supponga ad esempio che in un'applicazione venga utilizzata la classe Example in un assembly denominato Utility.dll. Gli sviluppatori di Utility.dll possono decidere di eseguire il refactoring dell'assembly e di spostare nel corso del processo la classe Example in un altro assembly. Se la versione precedente di Utility.dll viene sostituita con la nuova versione di Utility.dll e con il relativo assembly complementare, l'applicazione in cui viene usata la classe Example avrà esito negativo perché non è possibile trovare la classe Example nella nuova versione di Utility.dll.

Gli sviluppatori di Utility.dll possono evitare questo problema inoltrando le richieste per la classe Example mediante l'attributo TypeForwardedToAttribute. Se l'attributo è stato applicato alla nuova versione di Utility.dll, le richieste della classe Example verranno inoltrate all'assembly in cui è ora contenuta la classe. e l'applicazione esistente continuerà a funzionare normalmente, senza ricompilazione.

Inoltrare un tipo

La procedura di inoltro dei tipi prevede quattro passaggi:

  1. Spostare il codice sorgente del tipo dall'assembly originale nell'assembly di destinazione.

  2. Nell'assembly in cui solitamente si trova il tipo usato, aggiungere un TypeForwardedToAttribute per il tipo che è stato spostato. Nel codice riportato di seguito viene illustrato l'attributo per un tipo denominato Example che è stato spostato.

     [assembly:TypeForwardedToAttribute(Example::typeid)]
    
     [assembly:TypeForwardedToAttribute(typeof(Example))]
    
  3. Compilare l'assembly in cui è ora contenuto il tipo.

  4. Ricompilare l'assembly originale del tipo, con un riferimento all'assembly in cui è ora contenuto il tipo. Se ad esempio si compila un file C# dalla riga di comando, usare l'opzione Riferimenti (opzioni del compilatore C#) per specificare l'assembly contenente il tipo. In C++ utilizzare la direttiva #using nel file di origine per specificare l'assembly contenente il tipo.

Esempio di inoltro dei tipi C#

Continuando dalla descrizione dell'esempio improbabile precedente, si supponga di sviluppare Utility.dll e di avere una classe Example. Utility.csproj è una libreria di classi di base:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsing>true</ImplicitUsing>
  </PropertyGroup>

</Project>

La classe Example fornisce alcune proprietà ed esegue l'override di Object.ToString:

using System;

namespace Common.Objects;

public class Example
{
    public string Message { get; init; } = "Hi friends!";

    public Guid Id { get; init; } = Guid.NewGuid();

    public DateOnly Date { get; init; } = DateOnly.FromDateTime(DateTime.Today);

    public sealed override string ToString() =>
        $"[{Id} - {Date}]: {Message}";
}

Si supponga ora che sia presente un progetto consumer ed è rappresentato nell'assembly Consumer. Questo progetto consumer fa riferimento all'assembly Utility. Ad esempio, crea un'istanza dell'oggetto Example e la scrive nella console nel file Program.cs:

using System;
using Common.Objects;

Example example = new();

Console.WriteLine(example);

Quando l'app consumer viene eseguita, restituisce lo stato dell'oggetto Example. A questo punto, non esiste alcun inoltro dei tipi perché Consuming.csproj fa riferimento a Utility.csproj. Tuttavia, lo sviluppatore dell'assembly Utility decide di rimuovere l'oggetto Example come parte di un refactoring. Questo tipo viene spostato in un file Common.csproj appena creato.

Rimuovendo questo tipo dall'assembly Utility, gli sviluppatori introducono una modifica che causa un'interruzione. Tutti i progetti consumer verranno interrotti quando vengono aggiornati all'assembly Utility più recente.

Anziché richiedere ai progetti consumer di aggiungere un nuovo riferimento all'assembly Common, è possibile inoltrare il tipo. Poiché questo tipo è stato rimosso dall'assembly Utility, sarà necessario che Utility.csproj faccia riferimento a Common.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsing>true</ImplicitUsing>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\Common\Common.csproj" />
  </ItemGroup>

</Project>

Il progetto C# precedente fa ora riferimento all'assembly Common appena creato. Può essere PackageReference o ProjectReference. L'assembly Utility deve fornire le informazioni sull'inoltro dei tipi. Per convenzione, le dichiarazioni di inoltro dei tipi vengono in genere incapsulate in un singolo file denominato TypeForwarders; considerare il file C# TypeForwarders.cs seguente nell'assembly Utility:

using System.Runtime.CompilerServices;
using Common.Objects;

[assembly:TypeForwardedTo(typeof(Example))]

L'assembly Utility fa riferimento all'assembly Common e inoltra il tipo Example. Se si compila l'assembly Utility con le dichiarazioni di inoltro dei tipi e si rilascia Utility.dll nel bin Consuming utilizzo, l'app consumer funzionerà senza essere compilata.

Vedi anche