Typweiterleitung in der Common Language Runtime

Durch Typweiterleitung können Sie einen Typ in eine andere Assembly verschieben, ohne Anwendungen, die die ursprüngliche Assembly verwenden, neu kompilieren zu müssen.

Angenommen, eine Anwendung verwendet die Example-Klasse in einer Assembly namens Utility.dll. Die Entwickler von Utility.dll könnten beschließen, die Assembly umzugestalten und bei diesem Vorgang die Example-Klasse in eine andere Assembly verschieben. Wenn die alte Version von Utility.dll durch die neue Version von Utility.dll und die zugehörige Assembly ersetzt wird, treten Fehler in der Anwendung auf, die die Example-Klasse verwendet, weil sie die Example-Klasse in der neuen Version von Utility.dll nicht finden kann.

Die Entwickler von Utility.dll können dies durch das Weiterleiten von Anforderungen an die Example-Klasse mit dem Attribut TypeForwardedToAttribute vermeiden. Falls das Attribut auf die neue Version von Utility.dll angewendet wurde, werden Anforderungen für die Example-Klasse an die Assembly weitergeleitet, die nun die Klasse enthält. Die vorhandene Anwendung funktioniert ohne Neukompilierung weiterhin normal.

Weiterleiten eines Typs

Die Weiterleitung eines Typs erfolgt in vier Schritten:

  1. Verschieben Sie den Quellcode für den Typ aus der ursprünglichen Assembly in die Zielassembly.

  2. Fügen Sie in der Assembly, in der sich der Typ ursprünglich befunden hat, ein TypeForwardedToAttribute-Objekt für den verschobenen Typ hinzu. Der folgenden Code veranschaulicht das Attribut für einen Typ namens Example, der verschoben wurde.

     [assembly:TypeForwardedToAttribute(Example::typeid)]
    
     [assembly:TypeForwardedToAttribute(typeof(Example))]
    
  3. Kompilieren Sie die Assembly, die jetzt den Typ enthält.

  4. Führen Sie für die Assembly, in der sich der Typ befand, eine Neukompilierung mit einem Verweis auf die Assembly durch, die jetzt den Typ enthält. Wenn Sie z. B. eine C#-Datei über die Befehlszeile kompilieren, geben Sie mit der Option Referenzen (C#-Compileroptionen) die Assembly an, die den Typ enthält. In C++ verwenden Sie die #using-Direktive in der Quelldatei, um die Assembly anzugeben, die den Typ enthält.

Beispiel für die C#-Typweiterleitung

Stellen Sie sich vor, Sie entwickeln die Datei Utility.dll und verfügen über eine Example-Klasse. Utility.csproj ist eine einfache Klassenbibliothek:

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

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

</Project>

Die Example-Klasse bietet einige Eigenschaften und setzt Object.ToString außer Kraft:

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}";
}

Stellen Sie sich nun vor, dass es ein verarbeitendes Projekt gibt, welches in der Consumer-Assembly vorhanden ist. Dieses verarbeitende Projekt verweist auf die Utility-Assembly. Als Beispiel instanziiert es das Example-Objekt und schreibt es in die Konsole in der Datei Program.cs:

using System;
using Common.Objects;

Example example = new();

Console.WriteLine(example);

Wenn die verarbeitende App ausgeführt wird, gibt sie den Zustand des Example-Objekts aus. Zu dem Zeitpunkt gibt es keine Typweiterleitung, da die Datei Consuming.csproj auf die Datei Utility.csproj verweist. Das Entwicklerteam der Utility-Assembly entscheidet sich jedoch, das Example-Objekt im Rahmen eines Refactoring zu entfernen. Dieser Typ wird in eine neu erstellte Common.csproj-Datei verschoben.

Durch das Entfernen dieses Typs aus der Utility-Assembly führt das Entwicklerteam einen Breaking Change ein. Alle verarbeitenden Projekte werden unterbrochen, wenn sie auf die neueste Utility-Assembly aktualisiert werden.

Die verarbeitenden Projekte müssen keinen neuen Verweis auf die Common-Assembly hinzufügen, sondern können den Typ weiterleiten. Da dieser Typ aus der Utility-Assembly entfernt wurde, muss die Utility.csproj-Datei auf die Common.csproj-Datei verweisen:

<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>

Das vorherige C#-Projekt verweist nun auf die neu erstellte Common-Assembly. Dies kann entweder eine PackageReference oder ProjectReference sein. Die Utility-Assembly muss die Typweiterleitungsinformationen bereitstellen. Je nach Konvention werden Typweiterleitungsdeklarationen normalerweise in einer einzelnen Datei mit dem Namen TypeForwardersgekapselt. Betrachten Sie die folgende C#-Datei TypeForwarders.cs in der Utility-Assembly:

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

[assembly:TypeForwardedTo(typeof(Example))]

Das Utility-Assembly verweist auf die Common-Assembly und leitet den Typ Example weiter. Wenn Sie die Utility-Assembly mit den Typweiterleitungsdeklarationen kompilieren und die Utility.dll-Datei in den Consuming-Bin ablegen, funktioniert die verarbeitende App ohne Kompilierung.

Siehe auch