我对smalltalk不是很熟悉,但我想做的是在初始化我自己的类时覆盖另一个“new”类。这和Class>>bindingOf有什么关系吗?
编辑:
我想要实现的是:如果ObjectA调用new,那么ObjectB将处理该请求。
ObjectB与ObjectA无关。
是否只需更改ObjectB的实现即可完成此操作?
编辑:本文中的ObjectB是一个ObjectTracer,我希望它的行为有点像ObjectA的包装类。我是否要使用类的方法字典来更改ObjectA的new实现?该如何更改?
编辑:以下是我想要实现的内容:
| obj |
obj := ObjectA new.
obj aMethod.真正发生的事情是,当new被发送到ObjectA时,它被ObjectB (包装器)提供的实现所取代,就像aka.nice和赫尔南在他们的回答中提到的那样,ObjectB让#doesNotUnderstand处理发送给ObjectA的消息。
从本质上讲,有没有可能我所需要的就是让ObjectB取代ObjectA的#new
发布于 2012-12-10 04:21:54
大多数跟踪器、分析器和采样器都监控系统的执行情况。如果你想开发一个实际修改系统的跟踪器,你必须非常了解它的动态,以避免附带影响。
这是跟踪器的基本公式:
在跟踪器的实例端实现#doesNotUnderstand: aMessage,就像这样的模板:
doesNotUnderstand: aMessage
"trace the selector then pass the message on"
| result |
aMessage arguments size = 0
ifTrue:
[result := object perform: aMessage selector]
ifFalse:
[result := object perform: aMessage selector withArguments: aMessage arguments].
^result在您的例子中,在#perform: send之前,您可能会访问ObjectA方法字典,并用另一个CompiledMethod替换"aMessage“CompiledMethod。要访问类的方法字典,只需在一些Smalltalk中发送# method dictionary或#>>即可。
你可以添加一个新的编译方法,甚至在你知道如何做反射时替换掉整个字典:
| methodDictionary |
methodDictionary := MethodDictionary new.
methodDictionary at: #basicNew put: (Object compilerClass new
compile: 'basicNew ^Point basicNew'
in: Object
notifying: nil
ifFail: []) generate.注您不需要将方法驻留在要计算的MethodDictionary中。参见#valueWithReceiver:参数的发送者:
最后,向新对象发送消息
anObjectA := MyTracer on: ObjectA new.
anObjectA example1.是的,您也可以将其放入ObjectA的新方法中。你最好阅读Smalltalk中关于反射的一些章节,因为Smalltalk是进行计算反思的最好平台,所以在web上有很多内容。
发布于 2012-12-10 00:06:48
不,#bindingOf:与编译方法相关。
可以通过几个方法访问的变量,如全局变量、类变量或池变量,通过在方法文字中存储相同的绑定来共享。绑定是一种关联,其键是变量名,值是变量值。
当您的代码使用变量时,该方法将#value发送到幕后的绑定,当您将一个值存储到变量中时,它将发送# value:
但是请注意,根据Smalltalk的风格,这些操作可能会在字节代码中进行优化,并替换为对绑定的第二个实例变量(值)的直接访问。
因此,编译器需要检索bindingOf: aSymbol才能访问任何共享变量,其中aSymbol是变量的名称。因为变量访问的范围取决于类(只有类及其子类才能访问类变量...),所以会查询该方法编译成的类以获取该信息。
如果你想覆盖YourClass中的实例创建,只需在类端覆盖#new (我们说的是YourClass class>>#new)。如果你使用Squeak/Pharo方言,大多数时候,你可以通过覆盖实例端的#initialize (YourClass>>#initialize)来实现特定的实例化,因为#new将调用#initialize。
编辑
如果你想用ObjectTracer捕捉#new到ObjectA的发送,你可以这样做:
| theTrueObjectA |
theTrueObjectA := ObjectA.
[Smalltalk globals at: #ObjectA put: (ObjectTracer on: ObjectA).
"insert the code you want to test here"
ObjectA new]
ensure: [Smalltalk globals at: #ObjectA put: theTrueObjectA].EDIT2最后一句话可以用ensure: [ObjectA xxxUnTrace]代替
然而,现代的squeak调试器是侵入性的,它本身会向ObjectTracer发送许多消息,导致其他调试器弹出...您应该首先打开一个首选项窗口并禁用logDebuggerStackToFile。
注意,涉及的机制是当一个对象不理解一条消息时,它会发送消息# does:。ObjectTracer重写了# pop :弹出一个调试器。
你可以子类化ProtoObject来安装你自己的#doesNotUnderstand:处理(就像在脚本或文件中写东西一样)。
还要注意,ObjectTracer #继承了Also : ProtoObject,ProtoObject本身响应了许多ProtoObject>>#doesNotUnderstand不会捕获的消息:
最后注意:我使用了上面的#来表示可以理解的消息。
EDIT 3:一种可能的解决方案是使用两个实例变量tracedObject和messageMapping定义一种新的ObjectTracer,并创建以下实例:
MessageInterceptor class>>on: anObject interceptMessages: aDictionary
"Create an interceptor intercepting some messages sent to anObject.
aDictionary keys define message selectors that should be intercepted.
aDictionary values define block of code that should be evaluated in place.
These blocks always take one argument for passing the traced Object,
plus one argument per message parameter.
snip..."
MessageInterceptor>>doesNotUnderstand: aMessage
mapping := messageMapping at: aMessage selector
ifAbsent:
["We don't intercept this message, let the tracedObject handle it"
^aMessage sendTo: tracedObject].
^mapping valueWithArguments: {tracedObject} , aMessage arguments例如,您可以这样使用它:
| interceptor |
interceptor := MessageInterceptor on: ObjectA interceptMessages:
({#new -> [:class | ObjectTracer on: class new]} as: Dictionary).
[Smalltalk globals at: #ObjectA put: interceptor.
"insert the code you want to test here"
ObjectA new yourself]
ensure: [interceptor xxxUnTrace].发布于 2017-02-13 04:04:05
对象可以理解的方法存储在该对象的类中。如果你想覆盖实例创建#new方法发送给一个类,你已经在这个类的类上操作过了,即它的元类
meta := ObjectA class.你可以在这里读到关于元类的内容:CSE 341: Smalltalk classes and metaclasses。
每个类,包括类(元类)的类,将它们的实例可以理解的消息存储在方法字典中,在Pharo中是称为methodDict的实例变量。如果希望对象能够接收消息,则必须在字典中插入一个CompiledMethod,它将作为消息发送结果执行。Smalltalk中的类有一个方便的编译方法,可用于在类中安装CompiledMethods,例如
ObjectA
compile: 'new
^ObjectB new'
classified: 'instance creation'如果你的ObjectA已经定义了#new方法,这将覆盖它。您必须缓存旧的CompiledMethod以保留它以供以后恢复。
在现实生活中,你会使用方法包装器来实现你想要在这里实现的东西,看看Wrappers to the Rescue。
https://stackoverflow.com/questions/13788585
复制相似问题