使用代理对象欺骗 Ruby 的大小写运算符 ===
我正在尝试创建一个代理对象,将几乎所有方法调用传输到子对象,本质上是委托者模式。在大多数情况下,我只是使用 BasicObject 并将带有 method_missing 的每个调用传递给子对象。到目前为止,一切都很好。
诀窍是,尽我所能,我无法欺骗 Ruby 的 case 运算符,所以我做不到:
x = Proxy.new(15)
Fixnum === x #=> false, no matter what I do
这当然会使任何 case x
操作失败,这意味着代理不能安全地移交给其他图书馆。
我一生都无法弄清楚 === 正在使用什么。代理对于我所知道的所有基于类的内省都工作得很好,这些内省都正确地传递给了子对象:
x.is_a?(Fixnum) #=> true
x.instance_of?(Fixnum) #=> true
x.kind_of?(Fixnum) #=> true
x.class #=> Fixnum
Module#===
只是做了某种无法实现的魔法吗?避免?
I'm trying to make a proxy object that transfers almost all method calls to a child object, essentially the delegator pattern. For the most part, I'm just using a BasicObject and passing every call with method_missing to the child object. So far, so good.
The trick is that try as I might, I can't fool Ruby's case operator, so I can't do:
x = Proxy.new(15)
Fixnum === x #=> false, no matter what I do
This of course makes any case x
operations fail, which means the proxies can't be handed off safely to other libraries.
I can't for the life of me figure out what === is using. The proxy works fine for all of the class-based introspection I know of, which is all correctly passed to the child object:
x.is_a?(Fixnum) #=> true
x.instance_of?(Fixnum) #=> true
x.kind_of?(Fixnum) #=> true
x.class #=> Fixnum
Is Module#===
just doing some kind of magic that can't be avoided?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
是的,确实如此。
Module#===
用 C 实现,直接检查对象的类层次结构。看起来没有办法欺骗它。Yeah, it is.
Module#===
is implemented in C, examining the object's class hierarchy directly. It doesn't look like there's a way to fool it.问题是它执行了
Fixnum === x
,这意味着===
方法是在Fixnum
上调用的,而不是在>x。您可以替换现有的所有
===
方法(并且还要注意何时引入新的===
方法),但这将是大量工作,并且相当脆弱。The problem is that it does
Fixnum === x
, which means that the method===
is called onFixnum
and not onx
. You could replace all===
methods in existence, (and also watch for when new===
methods are introduced), but that would be a lot of work, and quite fragile.<罢工>
我认为您正在寻找的是
Delegator
class.
您的 Proxy 类应该是 Delegator 类的子类,然后定义
__getobj__
和__setobj__
来获取和设置目标对象。算了,我自己试过了,不行。
编辑:
正如 grddev 提到的,技术问题是 Fixnum 正在发送 :=== 方法。然而,进一步思考,我认为 Ruby 目前的行为是正确的。由于 Delegator 应该是一个隐藏实现细节的抽象接口,因此 Proxy 的实例未被正确识别为 kind_of?固定编号。
如果您确实希望 Proxy 类成为一种 Fixnum,但希望用方法来修饰它,那么合乎逻辑的做法是子类化 Fixnum,或者创建一个 ProxyMethods 模块并扩展 Fixnum 的各个实例。
当然,由于您无法真正执行 Fixnum.new,因此您必须子类化 Fixnum 才能扩展单个实例,但一般规则仍然有效。
I think what you're looking for is the
Delegator
class.Your Proxy class should subclass the Delegator class, then define
__getobj__
and__setobj__
to get and set the target object.Forget that, I tried it myself and it doesn't work.
EDIT:
As grddev mentions, the technical issue is that Fixnum is being sent the :=== method. Thinking about it further, however, I think Ruby's current behavior is correct. Since a Delegator is supposed to be an abstract interface to hide implementation details, instances of Proxy are correctly not identified as a kind_of? Fixnum.
If you really want the Proxy class to be a kind of Fixnum but wish to decorate it with methods, the logical thing to do is to either subclass Fixnum, or create a ProxyMethods module and extend individual instances of Fixnum.
Of course, since you can't really do Fixnum.new, you'll have to subclass Fixnum in order to extend a single instance, but the general rule stands.
你可能应该搜索一下 BlankSlate
班级。该类从普通对象中剥离了大部分方法,并且该网站有一个简单的代理类示例,它将打印出所有被调用的方法。这应该能让您更好地了解正在发生的事情。抱歉,我无法给您更完整的答案,但我正在打电话。希望有帮助。
you should probably do a search for the BlankSlate
class. This class strips out most of the methods from a normal Object and the website has an example of a simple Proxy class that will print out all methods being called. This shoul give you a better picture of what is happening. Sorry I can't give you a fuller answer but I'm on my phone. Hope that helps.