实现接口而不创建实现(动态代理?)
我一直致力于创建自己的 IoC 容器以用于学习目的。在问了几个关于它们的问题后,我发现创建一个工厂来“解析”对象是最好的解决方案(请参阅此处的第三种解决方案)。用户Krzysztof Koźmic表明Castle Windsor实际上可以为您实现这一点。
我整个早上都在读 CW 的源码。我知道当调用 Resolve 时,它“返回接口”。这个接口如何“拦截”调用(因为后面没有实现)并调用它自己的方法?
我知道这里显然存在一些反射欺骗,这非常令人惊奇。我只是根本不知道“拦截”是如何完成的。我尝试自己在 git 上冒险进入兔子洞,但我迷路了。如果有人能指出我正确的方向,我将不胜感激。
另外 - 创建类型化工厂不会依赖于调用代码内的容器吗?在 ASP.NET MVC 术语中,这就是我的感受。
编辑:找到 Reflection.Emit..可以用这个吗?
EDIT2:我对此研究得越多,自动创建工厂听起来就越复杂。我可能最终会坚持使用重复的代码。
I've been working on creating my own IoC container for learning purposes. After asking a couple questions about them, I was shown that creating a factory to "resolve" the objects was the best solution (see third solution here). User Krzysztof Koźmic showed that Castle Windsor actually can implement this for you.
I've been reading the source of CW all morning. I know when Resolve is called, it "returns the interface". How does this interface "intercept" calls (since there is no implementation behind) and call it's own methods?
I know there's obviously some reflection trickery going on here and it's quite amazing. I'm just not at all user how the "interception" is done. I tried venturing down the rabbit hole myself on git, but I've gotten lost. If anyone could point me in the right direction it'd be much appreciated.
Also - Wouldn't creating a typed factory have the dependency on the container inside the calling code? In ASP.NET MVC terms, that's what it seems to me.
EDIT: Found Reflection.Emit... could this be what's used?
EDIT2: The more and more I look into this, the more complicated it sounds to automatically create factories. I might end up just sticking with the repetitive code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这里有两个独立的概念:
依赖注入仅仅实例化实现该接口的现有类。例如,您可能有一个实现 IMyServices 的 MyServices 类。 IoC 框架为您提供了多种方法来指定当您请求 IMyServices 时,它将解析为 MyServices 的实例。可能会使用一些 IL Emit 魔法来设置工厂或辅助方法,但实际实例只是您定义的类。
模拟允许您实例化一个实现接口的类,而无需实际必须编写该类的代码。正如您所想,这通常会使用 Reflection 和 IL Emit。通常,发出的 IL 代码相当简单,将大部分工作委托给用 C# 编写的方法。模拟的大部分复杂性与指定方法本身的行为有关,因为框架通常允许您使用流畅的语法指定行为。有些,例如 Moles,只是让您指定一个委托来实现该方法,尽管 Moles 还可以做其他更疯狂的事情,例如将调用重定向到静态方法。
进一步详细说明,您实际上不需要使用 IL 来实现 IoC 功能,但这对于避免重复 Reflection 调用的开销通常很有价值,因为 Reflection 相对昂贵。 这里是有关温莎城堡正在做什么的一些信息。
为了回答您的问题,我发现最有帮助的地方是 OpCodes 类。这是对 IL 中可用功能以及操作码如何发挥作用的一个很好的总结。它本质上是一种基于堆栈的汇编语言(无需担心寄存器),但是是强类型的,并且具有对对象符号和概念(如类型、字段和方法)的一流访问权限。 这里是一篇很好的代码项目文章,介绍了 IL 的基础知识。如果您有兴趣,我还可以向您发送一些我在过去几年中创建的帮助程序类,这些类用于我自己的 Emit 代码。
There are two separate concepts here:
Dependency injection merely instantiates an existing class that implements the interface. For example, you might have a MyServices class that implements IMyServices. IoC frameworks give you various ways to specify that when you ask for an IMyServices, it will resolve to an instance of MyServices. There might be some IL Emit magic going on to set up the factory or helper methods, but the actual instances are simply classes you've defined.
Mocking allows you to instantiate a class that implements an interface, without actually having to code that class. This does usually make use of Reflection and IL Emit, as you thought. Typically the emitted IL code is fairly simple, delegating the bulk of the work to methods written in C#. Most of the complexity of mocking has to do with specifying the behavior of the method itself, as the frameworks often allow you to specify behavior with a fluent syntax. Some, like Moles, simply let you specify a delegate to implement the method, though Moles can do other, crazier things like redirecting calls to static methods.
To elaborate a bit further, you don't actually need to use IL to implement IoC functionality, but this is often valuable to avoid the overhead of repeated Reflection calls, since Reflection is relatively expensive. Here is some information on what Castle Windsor is doing.
To answer your question, the most helpful place I found to start was the OpCodes class. This is a good summary of the available functionality in IL and how the OpCodes function. It's essentially a stack-based assembly language (no registers to worry about), but strongly-typed and with first-class access to object symbols and concepts, like types, fields, and methods. Here is a good Code Project article introducing the basics of IL. If you're interested, I can also send you some helper classes I've created over the last few years that I use for my own Emit code.
类型工厂是使用 Castle DynamicProxy 库实现的。它动态生成一个实现该接口的类型,并将您通过该接口进行的所有对该类型的调用转发给拦截器。
它不会在您的代码中强加任何依赖性。该界面是在您控制的程序集中创建的,您不引用 Windsor。在其他程序集(应用程序的入口点)中,您告诉 Windsor 该接口并告诉它使其成为一个工厂,Windsor 会了解您的接口并使用它进行操作。这就是控制反转的荣耀。
其实没那么复杂:)
The typed factory is implemented using Castle DynamicProxy library. It generates a type on the fly that implements the interface and forwards all calls to that type, you make via the interface, to the interceptor.
It imposes no dependency in your code. The interface is created in your assembly, that you control, where you don't reference Windsor. In other assembly (entry point to the app) you tell Windsor about that interface and tell it to make it a factory and Windsor learns about your interface and does stuff with it. That's Inversion of Control in its glory.
It's actually not that complicated :)
ImpromptuInterface 基于接口创建 DLR 动态代理。它允许您使用静态接口进行动态实现。事实上,它甚至有一个基类
ImpromptuFactory
,它为创建具有基于接口的动态实现的工厂提供了起点。ImpromptuInterface creates DLR dynamic proxies based on interfaces. It allows you to have a dynamic implementation with a static interface. In fact it even has a baseclass
ImpromptuFactory
that provides the starting point for creating factories with a dynamic implementation based on the interface.