C# 动态类型未调用AppDomain.TypeResolve

C# 动态类型未调用AppDomain.TypeResolve,c#,reflection,.net-assembly,.net-4.6.1,C#,Reflection,.net Assembly,.net 4.6.1,Demorepo: 如何使用: 结帐 编撰 从控制台/bin/Debug删除TestLib.dll和TestLib.pdb 通过cmd执行console.exe 旧SO帖子: 给定的: 库中的类: namespace Test.TestLib { public class Class1 { } } 以及创建其实例的第二个类: namespace console { public class AnotherClass { public

Demorepo

如何使用

  • 结帐
  • 编撰
  • 从控制台/bin/Debug删除TestLib.dll和TestLib.pdb
  • 通过cmd执行console.exe
  • 旧SO帖子

    给定的

    库中的类:

    namespace Test.TestLib
    {
        public class Class1
        {
        }
    }
    
    以及创建其实例的第二个类:

    namespace console
    {
        public class AnotherClass
        {
            public void Create()
            {
                new Class1();
            }
        }
    }
    
    以及一个调用
    创建
    的控制台应用程序:

        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;
        }
    
    问题

    当行
    newanotherclass().Create()时,不会调用该事件被执行。而是引发一个异常:

    System.TypeLoadException:Der中的Der Typ“Test.TestLib.Class1” 程序集“TestLib,版本=1.0.0.0,区域性=中性, PublicKeyToken=null“konnte nicht geladen-werden

    比如:

    System.TypeLoadException: 程序集“TestLib,版本=1.0.0.0,区域性=中性, 无法加载PublicKeyToken=null

    请查看回购协议以获取完整示例

    //编辑: 演示项目是用VS2019 für.net461编写的。我认为dotnetcore的主要概念是相同的。让我知道你是否愿意与dotnetcore合作,这样我就可以为这两个平台提供项目

    //Edit2:

    我调试了IL代码,发现在调用Class1的构造函数之前,一切都正常运行: 所以我个人并不认为像Bruno所说的那样,事件处理程序插入得太晚

    官方文件规定,如果大会未知,则调用此事件:

    当公共语言运行库被激活时,将发生TypeResolve事件 无法确定可创建请求类型的程序集

    我以前没读过。希望有人能帮助我:-)

    //Edit3-可能的解决方案


    一种解决方法是基于类名列表创建类型。为了不破坏编译安全性,我可以使用不产生IL代码的
    nameof
    。在分支机构
    resolveType-solution1
    的回购协议中可以找到一个例子。但当然,这不是我想要的解决方案:-(

    我不是100%确定,但我想我已经弄明白了到底发生了什么

    在运行代码时,main在执行之前由JIT进行评估。这会导致尝试加载另一个类及其依赖项(因为所有内容最终都很可能是内联的,因为它非常小)

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

    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;
    
    我设法绕过了这个限制,在你的主要文章中没有提到这个类,而是替换了:

    new AnotherClass().Create();
    
    作者:

    诀窍是在您的回调插入AppDomain之后延迟类型的加载。通过使用反射,我屏蔽了真正用于JIT的类型,直到实际调用此代码为止


    要点:你不知道什么时候你的类型会被加载(事实上只要JIT读到提到你的类型的代码)。这意味着你必须尽快插入回调,并尽可能推迟包含提到你的类型的代码。

    调用
    type.GetType(“Test.TestLib.Class1”);
    将调用AppDomain.TypeResolve,但不调用AppDomain.AssemblyResolve,不确定为什么

    虽然AssemblyResolve回调插入得足够早,但在打印调试消息时可以在控制台上看到这一点。我已调试到IL代码中,在调用Class1的构造函数之前,一切都正常运行。请参见:I我不确定类型解析的内部结构。可能在加载程序集后,CLR知道包含哪些类型,并且不会再次请求它?我听说了一些关于静态程序集与动态程序集的内容。但是对于第二个程序集,通过事件进行类型解析应该可以工作:-(
    var type = Type.GetType("AnotherClass");
    var method = type.GetMethod("Create");
    var instance = type.GetConstructor(new Type[] { }).Invoke(new object[0]);
    method.Invoke(instance, new object[0]);