You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在程序集级别全局替换类实现?支持鸭子类型与单点配置

问题

希望在程序集级别替换通用类System.Collections.Generic.List<T>,让项目及引用的其他程序集中所有对该类的调用(比如var values = new List<int> { 1, 2, 3, 4, 5 })都自动使用自定义的List<T>实现。要求:

  • 无需修改多处代码引用,仅通过单点配置实现
  • 支持鸭子类型替换:自定义类无需继承原类,但需暴露相同的方法签名
  • 全局层面的解决方案,可通过反射或配置实现单点控制
  • 曾实现过动态替换方法,可参考动态向方法追加代码的思路

解决方案

方法1:类型转发 + 程序集绑定重定向

此方法通过CLR的类型转发机制,将对原类型的引用重定向到自定义类型,适合替换系统或第三方程序集中的类型。

步骤:

  1. 创建自定义List<T>类,确保方法签名、属性与原类完全一致(满足鸭子类型要求),命名空间保持为System.Collections.Generic。
  2. 在自定义类所在的程序集中添加类型转发属性:
[assembly: TypeForwardedTo(typeof(System.Collections.Generic.List<>))]
  1. 在项目配置文件(app.config/web.config/runtimeconfig.json)中添加程序集绑定重定向,让CLR优先加载你的自定义程序集:
<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="System.Collections" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="1.0.0.0" /> <!-- 自定义程序集版本 -->
      <codeBase version="1.0.0.0" href="YourCustomListAssembly.dll" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

局限性:

  • 自定义类必须与原类同名同命名空间
  • 系统程序集带有强签名,需额外处理签名替换,操作复杂度较高

方法2:编译时IL重写(Fody插件)

使用Fody框架编写自定义IL重写插件,在编译阶段自动替换所有对原List<T>的引用为自定义类型,无需修改业务代码。

步骤:

  1. 安装Fody及插件模板,创建自定义Fody插件项目。
  2. 编写插件核心逻辑,遍历程序集IL指令,替换类型引用:
public void ProcessModule(ModuleDefinition module)
{
    var originalListType = module.TypeSystem.GetType("System.Collections.Generic.List`1");
    var customListType = module.TypeSystem.GetType("YourNamespace.CustomList`1");

    foreach (var type in module.Types)
    {
        foreach (var method in type.Methods.Where(m => m.Body != null))
        {
            foreach (var instruction in method.Body.Instructions)
            {
                if (instruction.Operand is TypeReference tr && tr.FullName == originalListType.FullName)
                {
                    instruction.Operand = customListType;
                }
            }
        }
    }
}
  1. 在目标项目中配置Fody插件,编译时自动完成全局替换。

优势:

  • 完全无侵入,无需修改业务代码
  • 支持鸭子类型替换,自定义类无需继承原类
  • 一次性配置,所有引用自动生效

方法3:运行时自定义类型加载器

通过实现AssemblyLoadContext拦截类型加载请求,将原List<T>的加载重定向到自定义类型,适合运行时动态替换场景。

步骤:

  1. 继承AssemblyLoadContext,重写类型加载逻辑:
public class CustomTypeLoadContext : AssemblyLoadContext
{
    protected override Type LoadType(TypeName typeName)
    {
        // 匹配原List<T>的全名
        if (typeName.FullName == "System.Collections.Generic.List`1")
        {
            return typeof(YourNamespace.CustomList<>);
        }
        return base.LoadType(typeName);
    }
}
  1. 程序启动时使用自定义加载器加载所有相关程序集:
var loadContext = new CustomTypeLoadContext();
var targetAssembly = loadContext.LoadFromAssemblyPath("YourProject.dll");
// 执行程序入口
targetAssembly.EntryPoint.Invoke(null, new object[] { Environment.GetCommandLineArgs() });

优势:

  • 全局生效,覆盖所有类型加载场景
  • 支持鸭子类型替换,无需自定义类与原类同名
  • 单点配置,无需修改编译产物

局限性:

  • 需要通过自定义加载器加载所有目标程序集,已预加载的系统程序集可能无法拦截

方法4:运行时IL注入(基于动态方法替换)

结合你之前实现的动态方法修改思路,在程序启动时通过反射遍历所有方法,替换其中new List<T>()及方法调用的IL指令为自定义类型的对应逻辑。

核心思路:

  1. 遍历所有已加载程序集的类型和方法
  2. 解析方法IL指令,找到所有创建List<T>实例、调用List<T>方法的指令
  3. 将这些指令替换为自定义类型的实例创建、方法调用指令

注意:

此方式复杂度极高,需处理泛型重载、方法签名匹配等细节,适合深度定制场景,不推荐作为通用方案。


内容的提问来源于stack exchange,提问作者johnny 5

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 14:12:40