我正在使用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上下文。
但现在看来,我应该传递oAssemblyinstance evertwhere以使用加载程序集中的任何类型?
它是否加载了程序集,在那里我可以使用simpleType.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/

10-11 12:03