动态加载程序集:为什么这段代码有效?
在我的情况下,存在三个组件:Consumer
类、IExposedIface
接口和实现IExposedIface
的Expose
类。 Consumer
和 Exposed
均与 IExownedIface
静态链接,但 Consumer
不包含对 的编译时引用暴露
。
我正在尝试提出一个方案,允许消费者在运行时加载不同版本的暴露(取决于输入数据 - 假设每个输入文档都包含有关哪些信息)应使用 Exposed
版本来处理它)。为了实现这一目标,我开始研究AppDomains,现在我有了一个可以运行的基本版本。
到目前为止,在我看来,在向 Exposited
程序集提供 IExposedIface
程序集时,有两种选择。
仅在
实例,Consumer
的 bin 目录中拥有IExposeIface.dll
并处理AppDomain.AssemblyResolve
事件AppDomain
,其中我创建一个Exposed
在
Consumer
的 bin 目录中以及每个Exposed.dll
旁边都有IExposeIface.dll
。< /p>
现在考虑一下我针对此 IExposedIface
构建 Exposed
:
public interface IExposedIface
{
string SaySomething();
}
并且针对此 IExposedIface
构建 Consumer
:
public interface IExposedIface
{
string SaySomething();
string SaySomethingDifferent();
}
在第一种情况下, 例外情况
异常:方法“SaySomethingDifferent”位于“Exposed.Expose”类型中 程序集“Exposed,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”没有实现。
在我调用 appDomain.CreateInstanceAndUnwrap(...)
在新创建的 AppDomain 中创建 Expose
实例时抛出。
这对我来说看起来很合理。
但在第二种情况下,appDomain.CreateInstanceAndUnwrap(...)
运行得很好,我可以毫无问题地在检索到的对象上调用“SaySomething()”方法。一个例外
在接口/类型上找不到方法“SaySomethingDifferent”“IExposedIface.IExposedIface、IExposedIface、Version=2.0.0.0、Culture=neutral、PublicKeyToken=null”。
仅当我实际在 Consumer
中调用 SaySomethingDifferent()
时才会抛出。
我很惊讶在第二种情况下 CLR 让我走了这么远......有人可以解释为什么这是可能的吗?
In my situation there are three components: Consumer
class, IExposedIface
interface and Exposed
class implementing IExposedIface
. Both Consumer
and Exposed
are linked statically with IExposedIface
, but Consumer
bears no compile-time reference to Exposed
.
I am trying to come up with a scheme which would allow Consumer
loading different versions of Exposed
at runtime (depending on input data - let's say each input document carries an information about which version of Exposed
should be used to process it). To achieve this, I started studying AppDomains and now I have a basic version working.
So far it seems to me there are two options when it comes to providing IExposedIface
assembly to Exposed
assembly.
Having
IExposedIface.dll
only inConsumer
's bin directory and handlingAppDomain.AssemblyResolve
event for theAppDomain
in which I am creating an instance ofExposed
Having
IExposedIface.dll
both inConsumer
's bin directory as well as aside eachExposed.dll
.
Now consider that I build Exposed
against this IExposedIface
:
public interface IExposedIface
{
string SaySomething();
}
and I build Consumer
against this IExposedIface
:
public interface IExposedIface
{
string SaySomething();
string SaySomethingDifferent();
}
In the first case, the exception
Exception: Method 'SaySomethingDifferent' in type 'Exposed.Exposed' from
assembly 'Exposed, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
is thrown in the moment I call appDomain.CreateInstanceAndUnwrap(...)
to create an instance of Exposed
in the freshly created AppDomain.
That looks reasonable to me.
But in the second case, appDomain.CreateInstanceAndUnwrap(...)
goes through just fine and I can without problems call 'SaySomething()' method on the retrieved object. An exception
The method 'SaySomethingDifferent' was not found on the interface/type 'IExposedIface.IExposedIface, IExposedIface, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null'.
is only thrown when I actually call SaySomethingDifferent()
in Consumer
.
I was quite surprised that in this second case CLR had let me go so far...Could someone explain why this is possible?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
情况 #1 意味着 Expose.dll 绑定了错误版本的 IExposedIface.dll - 元数据加载器能够在加载程序集时检测到这一点,因为它发现了未实现的接口方法。
情况 #2(可能)意味着除了每个 Exposed.dll 之外,您还拥有每个 IExposedIface.dll 的正确版本,因此每个程序集都可以在其自己的 AppDomain 中加载。但是,AppDomain A 与 AppDomain B 具有不同的接口,这仅在调用实际跨越 AppDomain 边界时才会出现问题。
我建议不要尝试那些二进制兼容性游戏,而是进行适当的版本控制(即使用新方法创建一个新接口,继承旧接口,因此新版本的 IExposeIface.dll 确实向后兼容)。其他任何东西都很难调试,因为如果 Windows 可以访问 IExposeIface.dll 的两个版本,那么您可能会意外加载它们,然后您就有两个版本的 Type在 AppDomain 中造成无穷无尽的麻烦;)
Case #1 means that Exposed.dll is binding against the wrong version of IExposedIface.dll - the metadata loader is able to detect this when loading the assemblies because it finds an unimplemented interface method.
Case #2 (probably) means that you have the correct version of each IExposedIface.dll besides each Exposed.dll so each assembly can load within its own AppDomain. However AppDomain A has a different interface than AppDomain B which is only a problem when the call actually crosses the AppDomain border.
I'd suggest not trying those binary compatibility games and rather do proper versioning (ie. create a new interface with the new methods, inheriting from the old interface, so the new version of IExposedIface.dll is really backwards compatible). Anything else is really hard to debug because you can accidently end up loading both versions of IExposedIface.dll if they are reachable for windows, and then you have two versions of a Type in the AppDomain causing no end of trouble ;)