C# 动态类型未调用AppDomain.TypeResolve
Demorepo: 如何使用: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
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]);