没有为动态类型调用AppDomain.TypeResolve

时间:2019-07-09 08:27:26

标签: c# reflection .net-assembly .net-4.6.1

Demorepo

https://github.com/gabbersepp/csharp-dynamic-replace-class

使用方法

  1. 结帐
  2. 编译
  3. 从console / bin / Debug删除TestLib.dll和TestLib.pdb
  4. 通过cmd执行console.exe

旧版SO帖子

Replace existing class definition at runtime with newly created type

给出

库中的类:

namespace Test.TestLib
{
    public class Class1
    {
    }
}

还有另一个创建它的实例的类:

namespace console
{
    public class AnotherClass
    {
        public void Create()
        {
            new Class1();
        }
    }
}

还有一个调用create的控制台应用程序:

    static void Main(string[] args)
    {
        //...
        new AnotherClass().Create();
    }

请记住,只有Class1在额外的库中。其他两个类是相同的。

我想做什么

在运行时替换程序集:

        AssemblyName dynamicAssemblyName = new AssemblyName("TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        dynamicAssembly =
            AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
        var dynamicModule = dynamicAssembly.DefineDynamicModule("TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

但是我现在不想提供类型。相反,我使用:

AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;

    private static Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args)
    {
        Console.WriteLine("resolve type");
        if (args.Name.Contains("TestLib"))
        {
            dynamicModule.DefineType("Test.TestLib.Class1", TypeAttributes.Class | TypeAttributes.Public).CreateType();
            return dynamicAssembly;
        }

        return null;
    }

问题

在执行行new AnotherClass().Create();时不调用该事件。而是抛出异常:

  

System.TypeLoadException:der中的Der Typ“ Test.TestLib.Class1”   程序集“ TestLib,版本= 1.0.0.0,文化=中性,   PublicKeyToken = null“图片”

类似:

  

System.TypeLoadException:类型为“ Test.TestLib.Class1”的   程序集“ TestLib,版本= 1.0.0.0,文化=中性,   PublicKeyToken = null“无法加载

请查看回购中的完整示例。

//编辑: 该演示项目是使用VS2019 f.net461编写的。我认为dotnet核心的主要概念是相同的。让我知道您是否愿意使用dotnet core,以便为两个平台提供该项目。

// Edit2:

我调试了IL代码,发现一切正常,直到调用Class1的构造函数为止: enter image description here 因此,我个人并不认为事件处理程序会像Bruno所说的那样插入太晚。

官方文档指出,如果程序集未知,则调用此事件:

  

https://docs.microsoft.com/de-de/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8   当公共语言运行库为   无法确定可以创建所请求类型的程序集

我以前没有看过。希望有人可以帮助我:-)

// Edit3-可能的解决方案

一种解决方法是,根据类名列表创建类型。为了不降低编译安全性,我可以使用nameof,它不会产生IL代码。可以在分支resolveType-solution1的仓库中找到一个示例。但是,当然,不是我正在寻找的解决方案:-(

2 个答案:

答案 0 :(得分:1)

我不确定100%,但是我想我知道发生了什么事。

在运行代码时,您的主体在执行之前会先经过JIT评估。这导致尝试加载AnotherClass及其依赖项(因为所有内容都非常小,最终很可能会内联)。

这意味着在.net尝试找到您的类型之前,您将没有机会运行此代码:

... insert into Product values (@ProdName, @ProdPrice, @ProdDesc, ....

我设法通过替换以下内容而没有在您的主要课程中提及该类:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;

作者:

new AnotherClass().Create();

技巧是在将回调插入AppDomain中之后,减轻类型的负载。通过使用反射,我屏蔽了真正用于JIT的类型,直到实际调用此代码为止。

带走:您真的不知道何时加载您的类型(实际上,只要JIT读取提及您的类型的代码)。这意味着您必须尽快插入回调,并尽量减少包含提及您的类型的代码。

答案 1 :(得分:0)

调用Type.GetType("Test.TestLib.Class1");会调用AppDomain.TypeResolve而不是AppDomain.AssemblyResolve,但不确定为什么