消息传递和方法调用之间有什么区别?

时间:2010-08-25 01:40:06

标签: language-agnostic message-passing method-invocation

消息传递和方法调用之间是否存在差异,还是可以认为它们是等效的?这可能是语言特有的;许多语言不支持消息传递(尽管我能想到支持方法的所有语言),并且那些语言可以具有完全不同的实现。此外,方法调用存在很大差异,具体取决于语言(C vs. Java vs Lisp vs您最喜欢的语言)。我相信这与语言无关。对于使用调用方法无法执行的传递方法,您可以做什么,反之亦然(使用您最喜欢的语言)?

5 个答案:

答案 0 :(得分:9)

作为第一个近似值,答案是:none,只要你“行为正常”

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

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

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

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

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

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

答案 1 :(得分:7)

IIRC,他们已经被正式证明是等同的。至少表明他们应该这样做并不需要花费很多心思。关于所需要的只是暂时忽略被调用地址与内存中实际点的直接等价,并将其简单地视为一个数字。从这个角度来看,该数字只是一个抽象标识符,它唯一地标识您想要调用的特定类型的功能。

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

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

同时,不要搞错:事实上看起来就像成员函数调用一样,这并不妨碍它实际在另一台机器上执行或异步执行,或者(经常) 。实现此目的的典型机制是代理功能,它将成员函数调用的“虚拟消息”转换为“真实消息”,可以(例如)根据需要通过网络传输(例如,Microsoft的DCOM和CORBA都这样做)这很常规)。

答案 2 :(得分:6)

使用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中,编译器只是向您发出警告,将消息传递给它认为对象可能无法理解的对象,但忽略它并不会阻止程序执行。

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

改编自文章here。 有关详细信息,另请参阅this文章。

答案 3 :(得分:5)

他们在实践中确实不是一回事。消息传递是一种在两个或多个并行进程之间传输数据和指令的方法。方法调用是一种调用子例程的方法。 Erlang的并发性建立在前一概念及其并行定向编程的基础上。

消息传递最有可能涉及一种方法调用,但方法调用不一定涉及消息传递。如果确实如此,则会传递消息。消息传递是在并行进程之间执行同步的一种形式。方法调用通常意味着同步活动。调用者在继续之前等待方法完成。消息传递是协程的一种形式。方法调用是子程序的一种形式。

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

答案 4 :(得分:3)

  

消息传递和方法调用之间是否存在差异,还是可以认为它们是等效的?

他们很相似。一些差异:

可以同步或异步传递消息(例如Windows中SendMessage和PostMessage之间的差异)

您可能会在不确切知道要将其发送到哪个远程对象

的情况下发送消息

目标对象可能位于远程计算机或操作系统上。