Demorepo :
https://github.com/gabbersepp/csharp-dynamic-replace-class
使用方法:
旧版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
的构造函数为止:
因此,我个人并不认为事件处理程序会像Bruno所说的那样插入太晚。
官方文档指出,如果程序集未知,则调用此事件:
https://docs.microsoft.com/de-de/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8 当公共语言运行库为 无法确定可以创建所请求类型的程序集
我以前没有看过。希望有人可以帮助我:-)
// Edit3-可能的解决方案:
一种解决方法是,根据类名列表创建类型。为了不降低编译安全性,我可以使用nameof
,它不会产生IL代码。可以在分支resolveType-solution1
的仓库中找到一个示例。但是,当然,不是我正在寻找的解决方案:-(
答案 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,但不确定为什么