0. 结论
- LoadFile必须使用绝对路径,LoadFrom可以用绝对路径和相对路径
- LoadFile不会加载程序集所引用和依赖的其他程序集
- (未验证)LoadFrom载入前会检查是否已载入同名程序集,如果同一上下文中已载入不会重复载入/当作“数据文件”载入,即使版本不同
- 不同上下文可载入同名程序集,且会被视作不同类型
1. 示例1
- TestA 为底层库;
- TestB 引用 TestA;
- TestC 反射加载 TestB,TestB 与 TestC 不在同一路径。
1) LoadFile: TestC 通过 LoadFile 加载 TestB
LoadFile 必须使用绝对路径,否则会产生异常(需要绝对路径信息);
TestA 与 TestC 在同一路径:LoadFile("TestB.dll") 正常,执行引用TestA的方法MethodInfo.Invoke()正常;
TestA 与 TestC 不在同一路径:LoadFile("TestB.dll") 正常,执行引用TestA的方法MethodInfo.Invoke()异常(找不到TestA文件)。
2) LoadFrom: TestC 通过 LoadFrom 加载 TestB
LoadFrom 可以用绝对路径,也可以用相对路径;
TestA 与 TestC 在同一路径:LoadFile("TestB.dll") 正常,执行引用TestA的方法MethodInfo.Invoke()正常;
TestA 与 TestB 在同一路径:LoadFile("TestB.dll") 正常,执行引用TestA的方法MethodInfo.Invoke()正常;
TestA 与 TestC, TestB 均不在同一路径:LoadFile("TestB.dll") 正常,执行引用TestA的方法MethodInfo.Invoke()异常(找不到TestA文件)。
3) 重复LoadFrom: TestC 通过 LoadFrom 先加载 TestA,再加载TestB
// TestC静态引用TestA,TestB静态引用TestA,TestA2为TestA副本
// 以下代码AppDomain.CurrentDomain.GetAssemblies()结果:
// TestC;
// TestA;
// SubDir\TestB.dll;
LoadFrom(@".\SubDir\TestB.dll"); // 不重复加载已静态引用的TestA
// 以下代码AppDomain.CurrentDomain.GetAssemblies()结果:
// TestC;
// TestA;
// SubDir\TestA.dll;
// SubDir\TestB.dll;
LoadFrom(@".\SubDir\TestA.dll"); // 重复加载TestA
LoadFrom(@".\SubDir\TestB.dll");
// 以下代码AppDomain.CurrentDomain.GetAssemblies()结果:
// TestC;
// TestA;
// SubDir\TestA.dll;
// SubDir\TestB.dll;
LoadFrom(@".\SubDir\TestA.dll");
LoadFrom(@".\SubDir\TestB.dll");
LoadFrom(@".\SubDir\TestA2.dll"); // 不重复加载已加载的TestA
重复加载导致的类型匹配问题
顺序有关,先LoadFrom,则不可以,否则可以
在确定一个类型是否可强制转换为另一个类型时,路径很重要。即使包含类型的程序集相同,如果它们从不同的路径加载,它们也被视为不同的程序集,因此它们的类型也不同。这就是为什么使用加载上下文以外的上下文存在风险的原因之一。您可能会遇到以下情况:同一程序集在同一 appdomain 中多次加载(一次在 Load 上下文中,一次在 LoadFrom 上下文中,甚至两次在两个上下文中加载多次),并且它们的相应类型不可强制转换。
4) 类型(接口、特性)匹配查找问题
// 以下代码AppDomain.CurrentDomain.GetAssemblies()结果:
// TestC;
// TestA;
// SubDir\TestA.dll;
// SubDir\TestB.dll;
LoadFrom(@".\SubDir\TestA.dll");
LoadFrom(@".\SubDir\TestB.dll");
// 此时通过BaseType(GetCustomAttributes、GetInterfaces)从TestB中获取的TestA中类型(接口、特性) != 从TestC中获取的TestA中类型(接口、特性):
Console.WriteLine(type.BaseType == typeof(TestA.TestA)); // False
Console.WriteLine(type.GetInterface("TestA .ITestA") == typeof(TestA.ITestA)); // False
Console.WriteLine(type.GetCustomAttributes(typeof(TestA .TestAAttrubute), true) == typeof(TestA.TestAAttrubute)); // TestA.TestAAttrubute[0]
// 以下代码AppDomain.CurrentDomain.GetAssemblies()结果:
// TestC;
// TestA;
// SubDir\TestB.dll;
LoadFrom(@".\SubDir\TestB.dll");
// 此时通过BaseType(GetCustomAttributes、GetInterfaces)从TestB中获取的TestA中类型(接口、特性) != 从TestC中获取的TestA中类型(接口、特性):
Console.WriteLine(type.BaseType == typeof(TestA.TestA)); // True
Console.WriteLine(type.GetInterface("TestA .ITestA") == typeof(TestA.ITestA)); // True
Console.WriteLine(type.GetCustomAttributes(typeof(TestA .TestAAttrubute), true) == typeof(TestA.TestAAttrubute)); // TestA.TestAAttrubute[1]
2. 示例2
// path1 and path2 point to different copies of the same assembly on disk:
Assembly assembly1 = Assembly.LoadFrom(path1);
Assembly assembly2 = Assembly.LoadFrom(path2);
// These both point to the assembly from path1, so this is true
Console.WriteLine(assembly1.CodeBase == assembly2.CodeBase);
assembly1 = Assembly.LoadFile(path1);
assembly2 = Assembly.LoadFile(path2);
// These point to different assemblies now, so this is false
Console.WriteLine(assembly1.CodeBase == assembly2.CodeBase);
3. 参考
C#反射-Assembly.Load、LoadFrom与LoadFile进阶 - zagelover - 博客园 (cnblogs.com)
C#反射-Assembly.Load、LoadFrom与LoadFile详细例子用法_c# assembly.loadfrom相对路径
使用.NET程序集的LoadFile和LoadFrom之间的区别? - 问答 - 腾讯云开发者社区-腾讯云 (tencent.com)
Suzanne Cook's .NET CLR Notes | Microsoft Learn
Choosing a Binding Context | Microsoft Learn
C#加载程序集 - 掘金 (juejin.cn)
网友评论