Encaminhamento de tipo no Common Language Runtime

O encaminhamento de tipo permite que você mova um tipo para outro assembly sem ter que recompilar os aplicativos que usam o assembly original.

Por exemplo, suponha que um aplicativo use a classe Example em um assembly chamado Utility.dll. Os desenvolvedores de Utility.dll podem decidir refatorar o assembly e, durante o processo, podem mover a classe Example para outro conjunto. Se a versão antiga de Utility.dll for substituída pela nova versão de Utility.dll e por seu assembly complementar, o aplicativo que usa a classe Example falhará porque não foi possível localizar a classe Example na nova versão de Utility.dll.

Os desenvolvedores de Utility.dll pode evitar isso encaminhando solicitações para a classe Example, usando o atributo TypeForwardedToAttribute. Se o atributo tiver sido aplicado à nova versão do Utility.dll, as solicitações para a classe Example são encaminhadas ao assembly que agora contém a classe. O aplicativo existente continua a funcionar normalmente, sem recompilação.

Encaminhar um tipo

Há quatro etapas para encaminhar um tipo:

  1. Mova o código-fonte para o tipo do assembly original para o conjunto de destino.

  2. No assembly em que o tipo é usado para ser localizado, adicione um TypeForwardedToAttribute para o tipo que foi movido. O código a seguir mostra o atributo para um tipo chamado Example que foi movido.

     [assembly:TypeForwardedToAttribute(Example::typeid)]
    
     [assembly:TypeForwardedToAttribute(typeof(Example))]
    
  3. Compile o assembly que agora contém o tipo.

  4. Recompile o assembly onde o tipo estava localizado, com uma referência ao assembly que agora contém o tipo. Por exemplo, se você estiver compilando um arquivo em C# na linha de comando, use a opção Referências (opções do compilador C#) para especificar o assembly que contém o tipo. Em C++, use a diretiva #using no arquivo de origem para especificar o assembly que contém o tipo.

Exemplo de encaminhamento de tipo C#

Continuando na descrição de exemplo inventada acima, imagine que você está desenvolvendo o Utility.dll e tem uma classe Example. O Utility.csproj é uma biblioteca de classes básica:

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

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

</Project>

A classe Example fornece algumas propriedades e substituições 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}";
}

Agora, imagine que haja um projeto de consumo e ele seja representado no assembly Consumer. Esse projeto de consumo faz referência ao assembly Utility. Por exemplo, ele instancia o objeto Example e o grava no console em seu arquivo Program.cs:

using System;
using Common.Objects;

Example example = new();

Console.WriteLine(example);

Quando o aplicativo de consumo for executado, ele gerará o estado do objeto Example. Neste ponto, não há nenhum encaminhamento de tipo, pois Consuming.csproj faz referência a Utility.csproj. No entanto, os desenvolvedores do assembly Utility decidem remover o objeto Example como parte de uma refatoração. Esse tipo é movido para um Common.csproj que acaba de ser criado.

Ao remover esse tipo do assembly Utility, os desenvolvedores estão introduzindo uma alteração interruptiva. Todos os projetos de consumo serão interrompidos quando forem atualizados para o assembly Utility mais recente.

Em vez de exigir que os projetos de consumo adicionem uma referência ao assembly Common, você pode encaminhar o tipo. Como esse tipo foi removido do assembly Utility, você precisará fazer com que Utility.csproj faça referência 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>

O projeto C# anterior agora faz referência ao assembly Common que acaba de ser criado. Pode ser PackageReference ou ProjectReference. O assembly Utility precisa fornecer as informações de encaminhamento de tipo. Por convenção, as declarações de encaminhamento de tipo geralmente são encapsuladas em um só arquivo chamado TypeForwarders. Considere o seguinte arquivo C# TypeForwarders.cs no assembly Utility:

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

[assembly:TypeForwardedTo(typeof(Example))]

O assembly Utility faz referência ao assembly Common e encaminha o tipo Example. Se você quiser compilar o assembly Utility com as declarações de encaminhamento de tipo e remover Utility.dll para a lixeira Consuming, o aplicativo consumidor funcionará sem ser compilado.

Confira também