公共语言运行时中的类型转发

类型转发允许将类型移动到另一个程序集,而无需重新编译使用原始程序集的应用程序。

例如,假设应用程序在名为 Example的程序集中使用类。 Utility.dll 的开发人员可能会决定重构程序集,在此过程中,他们可能会将Example类移动到另一个程序集。 如果旧版本的 Utility.dll 替换为新版本 的Utility.dll 及其配套程序集,则使用该 Example 类的应用程序将失败,因为它无法在 Example 新版本 的Utility.dll中找到该类。

Utility.dll 的开发人员可以通过使用Example特性转发类TypeForwardedToAttribute的请求来避免这种情况。 如果已向新版本的 Utility.dll 应用了该属性,则对 类的请求会转发到该类目前所属的程序集Example。 现有应用程序继续正常运行,无需重新编译。

转发类型

转发某类型需要经过四个步骤:

  1. 将类型源代码从原始程序集移动到目标程序集。

  2. 在类型曾存于的程序集中,为已移动的类型添加 TypeForwardedToAttribute。 以下代码显示了已移动的类型 Example 的属性。

     [assembly:TypeForwardedToAttribute(Example::typeid)]
    
     [assembly:TypeForwardedToAttribute(typeof(Example))]
    
  3. 编译该类型目前所属的程序集。

  4. 重新编译原本包含该类型的程序集,并引用现在包含该类型的程序集。 例如,如果要从命令行编译 C# 文件,请使用 “引用 ”(C# 编译器选项) 选项指定包含该类型的程序集。 在C++中,使用源文件中的 #using 指令指定包含该类型的程序集。

C# 类型转发示例

继续上面的人为示例描述,假设你要开发 Utility.dll,并且你有一个 ExampleUtility.csproj 是一个基本类库:

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

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

</Project>

Example 类提供一些属性并且覆盖 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}";
}

现在,假设有一个进行中的项目,该项目在 Consumer 程序集中表示。 此消耗项目引用 实用工具 程序集。 例如,它会实例化Example对象并将其写入控制台Program.cs文件中:

using System;
using Common.Objects;

Example example = new();

Console.WriteLine(example);

当消费应用程序运行时,它将输出Example对象的状态。 此时,没有类型转发,因为 Consuming.csproj 引用 Utility.csproj。 但是, 实用工具 程序集的开发人员决定将对象作为重构的一部分删除 Example 。 此类型将移动到新创建的 Common.csproj

开发人员通过从 实用工具 程序集中删除此类型,从而引入重大改变。 所有依赖该程序集的项目在更新到最新的 实用程序集 时都会中断。

可以转发类型,而不是要求使用项目添加对 Common 程序集的新引用。 由于此类型已从实用工具程序集中删除,因此需要让 Utility.csproj 引用 Common.csproj

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

前面的 C# 项目现在引用新创建的 通用 程序集。 这可以是 PackageReferenceProjectReferenceUtility程序集需要提供类型转发信息。 按照惯例,类型前向声明通常封装在一个名为 TypeForwarders 的文件中,请考虑 Utility 程序集中的以下 TypeForwarders.cs C# 文件

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

[assembly:TypeForwardedTo(typeof(Example))]

实用工具程序集引用 Common 程序集,并转发类型Example。 如果将Utility程序集与类型转发声明一起编译,并将Utility.dll放置到Consuming bin中,使用的应用程序将可以运行而无需编译。

另请参阅