Smalltalk:消息的发件人是什么?

时间:2018-10-18 00:14:34

标签: oop message smalltalk

在smalltalk中,一切都通过向接收者对象发送消息来进行。它的语法通常遵循接收者消息的格式,其中接收者是消息发送到的对象。现在,我不禁要怀疑,smalltalk消息的发送者是什么?考虑以下smalltalk语句:

aMorph color: Color yellow

我可以将aMorph视为消息的接收者,但是发送者呢?标准的Smalltalk消息语法只有接收者和消息(选择器+参数),我无法确定发送者是什么,发送者在哪里。也许,一条消息实际上可以发送自己?

我记得浏览过一篇有关pharo smalltalk反射的文章,其中提到了消息的发件人,但是我找不到或理解什么是“发件人”。有人可以向我解释一下吗?谢谢。

5 个答案:

答案 0 :(得分:6)

每当发送消息时,就确定发送方并在运行时对其进行设置。从当前执行的方法的角度来看,它回答了“我们如何到达这里?”这一问题。在最常见的情况下,发送者将是发送消息导致当前方法被调用的任何方法。 (一个例外是 #doesNotUnderstand:处理程序,该处理程序会将邮件重定向到最初目标以外的其他地方),例如,在Squeak中,如果您在{{1 }},发件人将是 UndefinedObject >> DoIt 。如果您从 MyObject >> myTestSender 发送了相同的消息,则发件人将为 MyObject >> myTestSender

现在,假设您将aMorph color: Color yellow包装在MyProxyObject的实例proxy object aMorph中,其myProxy方法将接收到的所有内容转发给基础{{1} }对象。在这种情况下,当您执行doesNotUnderstand:时,发件人将为 MyProxyObject >> doesNotUnderstand:。 (除非您的aMorph方法进一步操纵运行时...如果需要,可以执行此操作)这实际上是一个很好的示例,说明何时可能需要查看谁是myProxy color: Color yellow的发送者:它正在被调用,但是您不知道从哪里来,因为代理添加了一个对您来说可能并不明显的间接级别。

因此,要查看发件人是谁,可以将以下内容添加到doesNotUnderstand:方法中:

#color:

从代码的角度来看,在 normal 代码执行期间,由Smalltalk运行时为您隐式处理发送者。除非对某些代码进行故障排除或在运行时需要内省或更改某些内容,否则您将不会经常查看发送者。

现在,这可能会引发一个问题:“ color:到底是什么?”这是代表调用栈顶部的特殊变量,很多人一开始很难理解。有关更多信息,请参见How does Smalltalk manipulate call stack frames

附录(希望这将消除Leandro的答案和我的答案之间的任何混淆)

Leandro的答案是将发件人作为一个通用术语,并考虑了更大的历史背景,而我的则是一个更现代的Squeak / Pharo中心,并且具有非常特殊的含义。我同意莱安德罗的观点,即发件人一词是模棱两可的,并且在各个实现中均未标准化(正如我们的不同答案所证明。)只是为了进一步混淆,在Blue Book引用发件人正在谈论发送上下文...既不是Transcript show: thisContext sender asString. 也不是thisContext。但是,问题注释中提到的链接的含义是明确的(即self),这是在引用Squeak / Pharo代码时通常所指的含义。因此,哪个答案是正确的取决于您是在查看特定的Smalltalk实现(在这种情况下,正确的用法是您使用的实现所决定的用法)还是作为一个更笼统的术语(不讨论特定的Smalltalk实现)(在这种情况下,莱安德罗(Leandro)是正确的:由于其用法已超负荷到接近无意义,因此需要对其进行解释)

答案 1 :(得分:4)

您可以认为发件人是self。换句话说,当方法被激活时(即在执行过程中),由self表示的对象可以解释为方法主体中发送的所有消息的发送者。

例如考虑方法#paint:,在OurClass中定义为

paint: aMorph
  aMorph color: Color yellow

一旦执行此方法,接收到OurClass消息的paint:的(子)实例将变为self。好吧,在此方法的激活过程中,self可以归因于color:的发送者的角色为aMorph

但是请注意,这仅是解释问题。例如,您还可以考虑正在执行的进程,并将发送方标识为激活#color:的进程框架。

虽然两种解释均有效,但事实是,在Smalltalk中,发件人的概念无关紧要,因为发送消息的行为是原始的,即是在虚拟机中实现的,而不是在虚拟映像中实现的。

当然,出于通信目的,将角色分配给某人甚至谈论发件人都是很有用的。但是隐含的对象取决于上下文。例如,在调试时,您将使用调用帧来标识发件人。但是,由于消息发送是“神奇地”发生的,因此实际上没有必要将发件人的角色附加到任何对象上。

即使在Bee Smalltalk中,由于没有VM,您也可以到达运行时的内部,发送方的概念也很繁琐,而且不必要。从技术上讲,Bee中的每个发送对象都有一个SendSite对象,该对象执行发送消息所需的所有步骤(PIC,查找等)。由于您可以检查这些对象并将消息发送给它们,因此可以推测在Bee中,发件人是SendSite。但同样,这有待解释。实际上,sender类中没有SendSite ivar,仅仅是因为Smalltalk语义不需要这种东西。

附录

当我说sender的概念需要解释时,我的意思是说,在发送机制的任何实现中都没有使用这种概念。更准确地说,执行发送的(原始)代码由执行方法查找的缓存例程组成,该例程仅考虑receiver的{​​{1}}和behavior,而无视“发件人”。

还请注意,从“调用方”获得的消息发送的主要“数据”是参数。而且,如果我们深入研究所有这些的机器代码实现,我们可能会争辩说另一个是返回地址,该地址用于链接框架。这就是为什么我提到“发件人”这一概念,它被称为调用者进程框架,这对于在调试器的实现中对其进行修正很有意义。

我的观点是,在Smalltalk中,selector没有明确的定义,这就是为什么与senderreceiver,{ {1}},selectorarguments

确实可以使用伪变量behavior来获取当前激活的method send。如果这样做,您将在调用框架中获得模拟thisContext的对象,这是sender的另一种解释。即使通过引用该对象可以使用它提供更多功能,self对象和消息发送机制仍将缺少sender

答案 2 :(得分:3)

如果您对Smalltalk的工作方式感兴趣,请查看@blihp和@ leandro-caniglia的答案。 Deep Into Pharo 14.5上下文:表示方法执行)也具有有关Context(名称为MethodContext,直到法鲁3的信息)的信息。

如果要尝试使用它,至少在Pharo中,伪变量thisContext可以访问当前执行点。您可以输入:

thisContext copy inspect.
在您的方法中

可以查看有关特定执行点的信息。此信息包括发件人。

但是,如果您想知道是否应该定期使用您的方法访问邮件的发件人,答案是。如果您需要了解使用常规方法发送消息的对象,则将发送者(self)作为附加参数传递。

答案 3 :(得分:0)

您已将aMorph标识为邮件的收件人。 现在,aMorph做什么? 它向各种事物发送消息。 当aMorph回应它收到的消息时, 它是发件人。它是接收者,它成为发送者。 完成aMorph后,它将不再是发件人,并向正在发送消息的人提供答复。

当然,每次aMorph发送消息时,接收者都会在确定答案的同时成为发送者。

以此类推。

答案 4 :(得分:0)

在消息anObject bar下方清楚地表明,发送消息的人是SomeClass instance。但是在响应消息的方法中,您必须求助于thisContext的服务。

SomeClass>>foo
    | anObject |
    anObject := AnotherClass new.
    anObject bar

AnotherClass>>bar
    | context senderObject receiver |
    context := thisContext.
    senderObject := context sender receiver.
    receiver := self