[英]Smalltalk: What is the sender of a message?
在 smalltalk 中,一切都是通過向接收者對象發送消息來發生的。 它的語法通常遵循格式接收者消息,其中接收者是消息發送到的對象。 現在我忍不住想知道,smalltalk 消息的發送者是什么? 考慮以下 smalltalk 語句:
aMorph color: Color yellow
我可以將 aMorph 視為消息的接收者,但是發送者呢? 標准的 smalltalk 消息語法只有接收者和消息(選擇器 + 參數),我無法識別發送者的位置和位置。 或者,一條消息實際上可以發送自己?
我記得在 pharo smalltalk 中瀏覽了一篇關於反思的文章,其中提到了消息的發送者,但我無法找到或理解這個“發送者”是什么。 任何人都可以向我解釋這一點嗎? 謝謝。
無論何時發送消息,都會在運行時確定並設置發送方。 從當前執行的方法的角度來看,它回答了“我們是如何到達這里的?”的問題。 在最常見的情況下,發送者是發送消息的任何方法,導致當前方法被調用。 (一個例外是一個#doesNotUnderstand:處理重定向消息,一些地方比原先預期的目標等)在佳樂例如,如果你在大一aMorph color: Color yellow
從工作區,將發件人UndefinedObject >> DOIT . 如果您從MyObject>>myTestSender發送了相同的消息,則發件人將是MyObject>>myTestSender 。
現在假設您將aMorph
包裝在代理對象myProxy
,這是 MyProxyObject 的一個實例,它的doesNotUnderstand:
方法將它接收到的所有內容轉發給底層的aMorph
對象。 在這種情況下,當您執行myProxy color: Color yellow
,發送方將是MyProxyObject>>doesNotUnderstand: 。 (除非您的doesNotUnderstand:
方法進一步操作運行時……如果需要,它可以這樣做)這實際上是一個很好的示例,說明何時您可能需要查看#color:
的發送者是#color:
:它正在被調用,但您不知道從哪里開始,因為代理添加了一個對您來說可能並不明顯的間接級別。
因此,為了查看發件人是誰,您可以在color:
方法中添加以下內容:
Transcript show: thisContext sender asString.
從您的代碼的角度來看,處理發送者是隱式的,並且在正常代碼執行期間由 Smalltalk 運行時為您處理。 除非您正在對某些代碼進行故障排除或以其他方式需要在運行時內省或更改事物,否則您不會經常查看發送者。
現在這可能會引發一個問題“ thisContext
到底是什么?” 它是一個特殊的變量,代表調用堆棧的頂部,很多人一開始都很難理解它。 有關更多信息,請參閱Smalltalk 如何操作調用堆棧幀。
附錄(希望這會消除 Leandro 的答案和我的答案之間的任何混淆)
Leandro 的回答是將發件人視為一個通用術語並考慮更大的歷史背景,而我的回答是更現代的以 Squeak/Pharo 為中心的,並且具有非常具體的含義。 我同意 Leandro 的觀點,即術語“發送者”是模棱兩可的,並且在實現中沒有標准化(正如我們不同的答案所證明的那樣。)只是為了進一步混淆水,在藍皮書中對發送者的引用正在談論發送上下文......這是既不是self
也不是thisContext sender
。 但是,問題的評論中提到的鏈接在其含義上是明確的(即thisContext sender
),正如在提及 Squeak/Pharo 代碼時通常所指的那樣。 因此,哪個答案是正確的取決於您是在查看特定的 Smalltalk 實現(在這種情況下,正確的用法是您正在使用的實現決定的任何一個)還是作為談論沒有特定 Smalltalk 實現時的更籠統的術語(在這種情況下,Leandro 是正確的:由於它的用法已經超載到幾乎毫無意義,因此需要對其進行解釋)
您可以將發件人視為self
。 換句話說,當一個方法被激活時(即在其執行期間), self
表示的對象可以被解釋為方法主體中發送的所有消息的發送者。
例如,考慮方法#paint:
,在OurClass
中定義為
paint: aMorph
aMorph color: Color yellow
一旦執行此方法,接收paint:
消息的OurClass
的(子)實例將變為self
。 好吧,在此方法激活期間, self
可以歸因於color:
發送者的角色color:
aMorph
。
但是請注意,這只是一個解釋問題。 例如,您還可以考慮正在執行的流程並將發送方標識為激活#color:
的流程框架。
雖然這兩種解釋都是有效的,但實際情況是,在 Smalltalk 中,發送者的概念是無關緊要的,因為發送消息的行為是原始的,即在虛擬機中實現,而不是在虛擬映像中。
當然,出於溝通目的,將該角色分配給某人甚至談論發件人是很有用的。 但隱含的對象取決於上下文。 例如,在調試時,您將使用調用幀標識發送方。 但是,由於消息發送“神奇地”發生,因此實際上沒有必要將發送者的角色附加到任何對象。
即使在 Bee Smalltalk 中,由於沒有 VM,因此您可以訪問運行時的內部結構,發送者的概念也很繁瑣且相當沒有必要。 從技術上講,Bee 中的每個發送都有一個SendSite
對象,它執行發送消息所需的所有步驟(PIC、查找等)並且由於您可以檢查這些對象並向它們發送消息,因此您可以推測 Bee 中的發送者是SendSite
。 但同樣,這是有待解釋的。 事實上,在SendSite
類中沒有sender
ivar 僅僅是因為 Smalltalk 語義不需要這樣的東西。
當我說sender
的概念可以解釋時,我的意思是這樣的概念沒有用於發送機制的任何實現。 更准確地說,執行發送的(原始)代碼由執行方法查找的緩存例程組成,該例程僅考慮receiver
的behavior
和selector
,而忽略“發送者”。
另請注意,消息發送從“調用者”獲取的主要“數據”是參數。 如果我們深入挖掘所有這些的機器代碼實現,我們可能會爭辯說另一個是返回地址,用於鏈接幀。 這就是為什么我提到“發送者”的概念是指調用者進程框架,這對於它在調試器的實現中的具體化很有意義。
我的觀點是,在 Smalltalk 中沒有明確的sender
定義,這就是為什么與相關概念(如receiver
、 selector
、 arguments
、 behavior
和method send
相比時很難識別它的原因。
確實可以使用偽變量thisContext
來獲取當前激活的sender
。 如果這樣做,您將在調用幀中獲得模擬self
的對象,這是sender
的另一種解釋。 即使通過對該對象的引用,您可以利用它來提供更多功能,但sender
仍將不在Message
對象和消息發送機制中。
如果您對 Smalltalk 的工作方式感興趣,請查看 @blihp 和 @leandro-caniglia 的答案。 Deep Into Pharo ( 14.5 Contexts:代表方法執行)也有關於Context
信息(在 Pharo 3 之前被命名為MethodContext
)。
如果你想試驗它,至少在 Pharo 中,偽變量thisContext
可以訪問當前執行點。 你可以把:
thisContext copy inspect.
在您的方法中查看您可以獲得關於特定執行點的哪些信息。 此信息包括發件人。
但是,如果您想知道是否應該定期訪問方法中消息的發送者,答案是否定的。 如果您需要知道在常規方法中發送消息的對象,請將發送者 ( self
) 作為附加參數傳遞。
您已將 aMorph 標識為消息的接收者。 現在,aMorph 做了什么? 它向各種事物發送消息。 當 aMorph 響應它所接收到的消息時,它是發送者。 它是接收者,它變成了發送者。 當 aMorph 完成后,它就不再是發送者,而是對發送它正在處理的消息的任何內容給出答案。
當然,每次 aMorph 發送消息時,接收者都會成為發送者,同時計算出答案。
等等。
在消息下方的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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.