我正在使用Assembly.LoadFrom()
加载程序集,因为程序集位于与应用程序基目录不同的路径中。
Dim oAssembly As Assembly = _
Assembly.LoadFrom("C:\\MyFolder\\" + ddlXlate.SelectedItem.ToString() + ".dll")
我从那个组件中使用一个
Type
没有任何问题:oXML = CType(oAssembly.CreateInstance(sBaseType + ".XlateContainer"), _
XlateBase.XlateContainer)
但是,当我尝试从另一个方法(如下面的方法)中使用此程序集中的
Type
时,会出现问题:oComboBox.DataSource = _
[Enum].GetValues(Type.GetType(sType + "+ItemEnum," + sAssemblyName))
sAssemblyName
是我实际使用LoadFrom()
加载的。在它说找不到程序集之后,我使用了AssemblyResolve
事件来解决我的问题:订阅
AssemblyResolve
事件:AddHandler AppDomain.CurrentDomain.AssemblyResolve, _
AddressOf MyResolveEventHandler
事件处理程序方法:
Private Shared Function MyResolveEventHandler(ByVal sender As Object, _
ByVal args As ResolveEventArgs) As Assembly
Return Assembly.LoadFrom("C:\\PSIOBJ\\" + args.Name + ".dll")
End Function
我想可能是因为它找不到我已经使用
LoadFrom()
加载的程序集清单文件中定义的依赖程序集而发生错误,但是当我检查args.Name
时,我看到它正在尝试加载相同的程序集,之后它工作正常,没有任何问题。因此,基本上在事件添加更改之前,在加载的程序集中找不到类型。我的旧代码使用
AppDomain.CurrentDomain.Load()
和Assembly.Load()
方法,它们在没有AssemblyResolve
事件的情况下运行良好。我能够从同一个Assembly
中的每个地方访问动态加载AppDomain
中的类型。LoadFrom()
可以在同一请求的程序集路径中自动找到依赖项,这不成问题,因为dll
需要的所有东西都在那里。所以在我看来,一开始它看起来像一个AppDomain
问题,因为它似乎可以从Load
上下文而不是LoadFrom
上下文访问程序集,我现在使用LoadFrom
上下文。但现在看来,我应该传递
oAssembly
instance evertwhere以使用加载程序集中的任何类型?它是否加载了程序集,在那里我可以使用simple
Type.GetType(...)
方法到处访问它(同一个appdomain)?有人能填一下漏掉的分数并回答我的问题吗?
你可以用c_,事实上我不喜欢vb.net,但我必须在办公室里用它。
最佳答案
如果我正确地理解了你的问题,你正试图按照以下思路来做:
var asm = Assembly.LoadFrom(@"D:\Projects\_Libraries\FluentNH 1.1\Castle.Core.dll");
var obj = asm.CreateInstance("Castle.Core.GraphNode");
var type = Type.GetType(obj.GetType().AssemblyQualifiedName, true); // fails
您遇到的问题是,无论您使用何种形式的程序集加载,当库与您的可执行文件不在同一路径时,变量
type
总是null
。原因
你遇到的是different loading contexts for assemblies in .NET的问题。通常有三种,实际上是四种类型的加载上下文,简而言之:
默认加载上下文。这用于GAC中的所有程序集、当前执行的程序集、当前路径中的程序集(请参见
BaseDirectory
)和privatepath中的程序集(请参见RelativeSearchPath
)。Assembly.Load(string,..)
使用此上下文。从上下文加载。这是用于磁盘上不在探测路径中的任何程序集的上下文,通常用
Assembly.LoadFrom
加载。只反映上下文。无法执行此上下文中的类型。
无上下文上下文。使用
Assembly.Load(byte\[\],..)
和Assembly.LoadFile
方法加载程序集时,或加载未保存到磁盘的动态程序集时,将使用此上下文。在一个上下文中加载的类型与另一个上下文不兼容(您甚至不能将相等的类型从一个上下文强制转换到另一个上下文!)。特别在一个上下文上操作的方法,无法访问另一个上下文。
Type.GetType(string)
只能在默认上下文中加载类型,除非您稍微帮助该方法。这正是你遇到的。当程序集dll位于应用程序的路径中时,一切正常。你一搬动它,一切就开始分崩离析。
更具体地说:
当您调用
Type.GetType(string)
时,它将查询路径中所有静态引用的程序集以及当前路径(AppDomain.BaseDirectory
)、GAC和AppDomain.RelativeSearchPath
和中动态加载的程序集。不幸的是,相对搜索路径必须是相对于基目录的。结果:
此行为的结果是GetType不只是检查所有加载的程序集。相反,它的工作方式相反,它确实:
gettype使用第一个参数的assembly部分来定位使用
Assembly.Load
的程序集。如果找到,则反映该程序集中的类型。
如果找不到,则返回null而不尝试任何其他操作(或者抛出
FileNotFoundException
,这可能会相当混乱)。您可以自己进行测试:
Assembly.Load
仅提供程序集名称时不起作用。解决
有几种解决办法。一个你已经给自己命名的程序集对象。还有一些,每个都有自己的缺点:
对实例化对象本身使用
GetType()
,而不是静态方法Type.GetType(string)
。这样做的好处是,您不需要类型的程序集限定名,这可能很难获得(在您的示例中,您没有说明如何设置sAssemblyName
,但这不也是您需要浮动的内容吗?)是的。使用检查加载的程序集并返回加载的程序集的泛型解析器。您不需要再次致电
LoadFrom
。我测试了以下几项,效果很好,速度也很快:// works for any loaded assembly, regardless of the path
private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
// you may not want to use First() here, consider FirstOrDefault() as well
var asm = (from a in AppDomain.CurrentDomain.GetAssemblies()
where a.GetName().FullName == args.Name
select a).First();
return asm;
}
// set it as follows somewhere in the beginning of your program:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
将appdomain.currentdomain.assemblyload和.assemblyResolve事件一起使用。第一个用于记忆字典缓存中每个加载的程序集(按全名),第二个用于通过按名称从字典中获取值来探测该字典。这是一个相对简单的实现,可能比以前的解决方案执行得稍微好一些。
使用
AppDomain.CurrentDomain.TypeResolve
事件处理程序。我还没试过,所以我不确定在你的场景中是否可行。getType首先尝试加载程序集,当加载失败时,它不会尝试解析该类型,并且此事件不会激发。将要解析的库添加到GAC或应用程序的任何(相对)路径。这是目前为止最简单的解决办法。
将路径添加到app.config。这只适用于强类型程序集,在这种情况下,您可以同样轻松地将它们加载到gac中。非强类型程序集必须仍位于当前应用程序的相对路径中。
结论
静态方法组
Type.GetType(..)
在第一次查看加载的程序集时表现得相当不直观。一旦您了解了多个上下文背后的思想,请尝试将程序集放置在默认上下文中。当这不可能时,您可以创建一个AssemblyResolve
事件处理程序,这并不难使其在一般情况下适用。关于c# - Assembly.LoadFrom之后的ResolveEventHandler,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9984171/