c# - 动态类型不调用 AppDomain.TypeResolve
问题描述
德莫雷波:
https://github.com/gabbersepp/csharp-dynamic-replace-class
如何使用:
- 查看
- 编译
- 从 console/bin/Debug 中删除 TestLib.dll & TestLib.pdb
- 通过cmd执行console.exe
旧的SO帖子:
给定:
lib中的一个类:
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 Typ "Test.TestLib.Class1" in der Assembly "TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" konnte nicht geladen werden
就像是:
System.TypeLoadException:无法加载程序集“TestLib,版本 = 1.0.0.0,文化 = 中性,PublicKeyToken = null”中的类型“Test.TestLib.Class1”
请查看 repo 以获取完整示例。
//编辑: 演示项目是用 VS2019 für .net461 编写的。我认为 dotnet core 的主要概念是相同的。如果您更愿意使用 dotnet core,请告诉我,以便我可以为这两个平台提供项目。
//编辑2:
Class1
我调试了IL代码并看到,在调用
构造函数之前一切都运行良好:
所以我个人认为事件处理程序插入的时间不会太晚,正如布鲁诺所说的那样。
官方文档指出,如果程序集未知,则调用此事件:
https://docs.microsoft.com/de-de/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8 当公共语言运行库无法确定可以创建请求类型的程序集时,会发生 TypeResolve 事件
我以前没有读过。希望有人可以帮助我:-)
//Edit3 - 可能的解决方案:
一种解决方法可能是根据类名列表创建类型。为了不降低编译安全性,我可以使用nameof
不产生 IL 代码的方法。可以在分支的 repo 中找到一个示例resolveType-solution1
。但当然,不是我正在寻找的解决方案:-(
解决方案
我不是 100% 确定,但我想我知道发生了什么。
运行您的代码时,您的 main 在执行之前由 JIT 评估。这导致尝试加载 AnotherClass 及其依赖项(因为所有内容都很可能最终被内联,因为它非常小)。
这意味着在 .net 尝试查找您的类型之前,您将没有机会运行此代码:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;
我设法绕过了这个限制,没有在你的 main 中提及这个类,方法是替换:
new AnotherClass().Create();
经过:
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]);
诀窍是在将回调插入 AppDomain 后延迟类型的加载。通过使用反射,我屏蔽了真正用于 JIT 的类型,直到实际调用此代码。
带走:您并不真正知道何时加载您的类型(事实上,只要 JIT 读取提及您的类型的代码)。这意味着您必须尽快插入回调并尽可能推迟包含提及您的类型的代码。
推荐阅读
- python - 使用请求安全处理潜在的恶意 URL
- vba - 显示运行时错误 2471 的访问表单中连接到按钮的代码问题
- python - Python中的SOLID
- javascript - NodeJS (Windows) - 语法错误:缺少 X
- c# - 在 C# 中返回 JSON 对象数组
- python - Pycharm上安装pygame的问题
- datetime - 如何删除 Tableau 中日期/时间字段中的时间?
- scala - 如何将 TraversableLike.to[Col] 与 Map 一起使用?
- javascript - 为什么我的 do-while 循环会变成无限循环?
- mapkit - 如何向注释添加按钮,当前解决方案不起作用