Reflection 截获吱吱作响的消息

Reflection 截获吱吱作响的消息,reflection,smalltalk,metaclass,squeak,objc-message-send,Reflection,Smalltalk,Metaclass,Squeak,Objc Message Send,我试图更好地理解Smalltalk中的反映。我使用的是Squeak的最新版本(v4.3)。我想截获发送到我的一个类实例的每条消息。我假设我可以用args:executeMethod覆盖方法ProtoObject>>,但Stéphane Ducasse向我解释说,出于性能原因,不使用此方法(这是我自己对其答案的总结)。我应该覆盖哪个方法/如何拦截发送的消息 以下是我尝试的代码: Object subclass: #C instanceVariableNames: 'i' class

我试图更好地理解Smalltalk中的反映。我使用的是Squeak的最新版本(v4.3)。我想截获发送到我的一个类实例的每条消息。我假设我可以用args:executeMethod覆盖方法
ProtoObject>>,但Stéphane Ducasse向我解释说,出于性能原因,不使用此方法(这是我自己对其答案的总结)。我应该覆盖哪个方法/如何拦截发送的消息

以下是我尝试的代码:

Object subclass: #C
    instanceVariableNames: 'i'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'CSE3009'.

C class compile: 'newWithi: anInt
    ^(self new) i: anInt ; yourself.'.

C compile: 'withArgs: someArgs executeMethod: aMethod
    Transcript show: ''Caught: ''.
    ^ super withArgs: someArgs executeMethod aMethod.'.

C compile: 'foo: aText
    Transcript show: aText.
    Transcript show: i.
    Transcript cr.'.

C compile: 'i: anInt
    i := anInt.'.

o := C newWithi: 42.
o foo: 'This is foo: '.
执行这整段代码将产生:

This is foo: 42
当我想要:

Caught: This is foo: 42

没有内置的方式来截获发送给这样的对象的消息。我们通常用两种方法来做这种把戏

首先,您可以创建一个包装器对象,该对象响应doesNotUnderstand:。该对象的超类通常为nil,因此它不会从该对象继承任何实例方法。处理程序会将其所有消息委托给目标对象。它可以在调用之前和之后执行代码。对原始对象的所有引用现在都指向新的“代理”对象。发送给self的消息不会被截获,代理需要测试返回self的对象,并将返回的对象改为代理

第二种方法是使用一种称为方法包装器的机制。方法包装器允许您将一组类中的所有方法替换为在调用原始方法之前和之后执行其他操作的方法。这种方法可以提供完全看不见的结果,并截获所有消息,包括发送给self的消息


MethodWrappers可用于VisualWorks和VASmalltalk。我相信它也适用于和Pharo,但我不确定。

三种主要技术是:

  • 动态代理
  • 方法包装器
  • 字节码检测
为了更好地比较所有可能的方法,请看一下斯蒂芬·杜卡斯(Stephane Ducasse)(显然,你已经认识他了)


感兴趣的还有F.Rivard的“”,它展示了如何使用字节码重写实现前置和后置条件。这也是拦截的一种形式

对于Squeak或PharoThank,有几种
MethodWrapper
实现供您回答,但我有点惊讶:我不应该通过反射来更改方法调用的语义吗?(这就是为什么我天真地认为我可以用args:executeMethod:
覆盖
ProtoObject>>。我的意思是,类似于覆盖新类方法的方式。在我看来,使用
的解决方案不理解:
工作,因为语言是非类型化的,
方法包装器
(和其他)是使用
doesNotUnderstand:
的解决方案的实现。Smalltalk是后期绑定的,因此在实际调用发生时确定接收者。Smalltalk也不会在接收者上调用方法,但它会向其发送一条更松散耦合的消息。MethodWrappers与doesNotUnderstand无关。这两种方法不同出租东西。使用doesNotUnderstand,您可以为类构建替换,而MethodWrappers是替换已编译方法的机制。此处使用的方法称为run:With:因为替换对象实现如果调用方法的唯一方法是调用另一个方法,我们将永远无法调用方法。Smalltalk虚拟机了解如何在方法字典中查找方法并调用它们。这是在VM级别完成的,是调用方法的默认机制。这不仅仅是性能问题。它绝对需要由VM完成,否则您将无法完成。Squeak的最新发布版本是4.4。谢谢@ewernli,我实际上已经读过了,但它们并没有直接解决我的问题。。。