36

消息传递和方法调用之间是否有区别,或者它们可以被认为是等效的?这可能是特定于语言的;许多语言不支持消息传递(尽管我能想到的所有语言都支持方法),而那些支持的语言可以有完全不同的实现。此外,根据语言(C 与 Java 与 Lisp 与您最喜欢的语言)的不同,方法调用也存在很大差异。我相信这是与语言无关的。你可以用一个传递的方法做什么,而你不能用一个调用的方法做,反之亦然(用你最喜欢的语言)?

4

5 回答 5

9

作为第一个近似值,答案是:没有,只要你“表现正常”

尽管许多人认为存在 - 从技术上讲,它通常是相同的:缓存查找要为特定命名操作执行的一段代码(至少对于正常情况)。将操作的名称称为“消息”或“虚拟方法”没有区别。

但是:Actor 语言确实不同:在拥有活动对象(每个对象都有一个隐式消息队列和一个工作线程 - 至少在概念上),并行处理变得更容易处理(谷歌也“通信顺序进程”更多)。

但是:在 Smalltalk 中,可以包装对象以使其像演员一样,而无需实际更改编译器、语法甚至重新编译。

但是:在 Smalltalk 中,当您尝试发送接收者不理解的消息(即“someObject foo:arg”)时,会创建一个包含名称和参数的消息对象,然后传递该消息对象作为“doesNotUnderstand”消息的参数。因此,对象可以自行决定如何处理未实现的消息发送(也称为未实现方法的调用)。它可以 - 当然 - 将它们推送到队列中,以便工作进程对它们进行排序......

当然,这对于静态类型语言来说是不可能的(除非你大量使用反射),但实际上这是一个非常有用的特性。代理对象、按需加载代码、远程过程调用、学习和自我修改代码、适应和自我优化程序、corba 和 dcom 包装器、工作队列都建立在该方案之上。它可能被滥用,并导致运行时错误 - 当然。所以它是一把双面剑。锋利而强大,但在初学者手中很危险......

编辑:我在这里写关于语言实现的文章(如 Java vs. Smalltalk - 不是进程间机制。

于 2010-08-25T02:07:51.320 回答
8

使用 Objective-C 作为消息的示例和使用 Java 作为方法的示例,主要区别在于,当您传递消息时,Object 决定它要如何处理该消息(通常会导致调用 Object 中的实例方法)。

然而,在 Java 中,方法调用是一个更静态的事情,因为您必须引用您正在调用该方法的类型的 Object,并且该类型中必须存在具有相同名称和类型签名的方法,或者编译器会抱怨。有趣的是实际调用是动态的,尽管这对程序员来说并不明显。

例如,考虑一个类,如

class MyClass {
    void doSomething() {}
}

class AnotherClass {
    void someMethod() {
        Object object = new Object();
        object.doSomething(); // compiler checks and complains that Object contains no such method.

        // However, through an explicit cast, you can calm the compiler down,
        // even though your program will crash at runtime
        ((MyClass) object).doSomething(); // syntactically valid, yet incorrect
    }
}

然而,在 Objective-C 中,编译器只是向您发出警告,告知您将消息传递给它认为该对象可能不理解的对象,但忽略它并不会阻止您的程序执行。

虽然这非常强大和灵活,但如果由于堆栈损坏而使用不当,可能会导致难以发现的错误。

改编自这里的文章。另请参阅本文了解更多信息。

于 2012-08-21T23:50:30.640 回答
8

IIRC,它们已被正式证明是等效的。至少表明它们应该是不需要太多的思考。所需要的只是暂时忽略被调用地址与内存中实际位置的直接等价性,并将其简单地视为一个数字。从这个角度来看,数字只是一个抽象标识符,它唯一地标识您希望调用的特定类型的功能。

即使您在同一台机器上调用函数,也没有真正要求被调用地址直接指定被调用函数的物理(甚至虚拟)地址。例如,尽管几乎没有人真正使用过它们,但英特尔保护模式任务门允许直接调用任务门本身。在这种情况下,只有地址的段部分被视为实际地址——即,对任务门段的任何调用最终都会调用相同的地址,而不管指定的偏移量如何。如果需要,处理代码可以检查指定的偏移量,并使用它来决定要调用的单个方法——但是指定的偏移量和被调用函数的地址之间的关系可以是完全任意的。

成员函数调用只是一种消息传递类型,它在所讨论服务的客户端和服务器共享公共地址空间的常见情况下提供(或至少有助于)优化。抽象服务标识符和该服务提供者所在地址之间的 1:1 对应关系允许从一个到另一个的微不足道的、异常快速的映射。

同时,不要误会:看起来像成员函数调用的事实并不能阻止它在另一台机器上实际执行或异步执行,或(经常)两者兼而有之。实现这一点的典型机制是代理函数,它将成员函数调用的“虚拟消息”转换为可以(例如)根据需要通过网络传输的“真实消息”(例如,Microsoft 的 DCOM 和 CORBA 都可以)这很常见)。

于 2010-08-25T04:26:38.260 回答
6

它们在实践中确实不是一回事。消息传递是一种在两个或多个并行进程之间传输数据和指令的方法。方法调用是调用子程序的一种方式。Erlang 的并发是建立在前一个概念之上的,它的面向并发编程。

消息传递很可能涉及某种形式的方法调用,但方法调用不一定涉及消息传递。如果是这样,那将是消息传递。消息传递是在并行进程之间执行同步的一种形式。方法调用通常意味着同步活动。调用者等待方法完成,然后才能继续。消息传递是协程的一种形式。方法调用是子程序的一种形式。

所有的子程序都是协程,但所有的协程都不是子程序。

于 2010-08-25T02:00:58.927 回答
3

消息传递和方法调用之间是否有区别,或者它们可以被认为是等效的?

他们很相似。一些区别:

消息可以同步传递也可以异步传递(例如Windows中SendMessage和PostMessage的区别)

您可能会在不确切知道要将其发送到哪个远程对象的情况下发送消息

目标对象可能在远程机器或操作系统上。

于 2010-08-25T01:53:52.090 回答