メッセージの受け渡しとメソッドの呼び出しに違いはありますか、それとも同等と見なすことができますか? これはおそらく言語に固有のものです。多くの言語はメッセージ パッシングをサポートしていません (私が考えることができるすべての言語はメソッドをサポートしています)。また、言語によってメソッド呼び出しに大きな違いがあります (C、Java、Lisp、お好みの言語)。これは言語に依存しないと思います。呼び出されたメソッドではできなくて、渡されたメソッドでできること、およびその逆 (お気に入りの言語で) は?
5 に答える
最初の概算として、答えは次のとおりです。「通常どおりに振る舞う」限り、なし
多くの人が存在すると考えていますが - 技術的には、通常は同じです: 特定の名前付き操作 (少なくとも通常の場合) のために実行されるコード片のキャッシュされたルックアップです。操作の名前を「メッセージ」または「仮想メソッド」と呼んでも違いはありません。
BUT: Actor 言語は本当に異なります: アクティブなオブジェクト (すべてのオブジェクトには暗黙的なメッセージ キューとワーカー スレッドがあります - 少なくとも概念的には) を持つことで、並列処理が扱いやすくなります (Google はさらに「逐次プロセスの通信」も行います)。
しかし、Smalltalk では、実際にコンパイラーや構文を変更したり、再コンパイルしたりすることなく、オブジェクトをラップしてアクターのようにすることができます。
しかし、Smalltalk では、受信者が理解できないメッセージ (つまり、"someObject foo:arg") を送信しようとすると、名前と引数を含むメッセージ オブジェクトが作成され、そのメッセージ オブジェクトが渡されます。 「doesNotUnderstand」メッセージの引数として。したがって、オブジェクトは、未実装のメッセージ送信 (未実装のメソッドの呼び出し) を処理する方法を自分自身で決定できます。もちろん、それらをワーカープロセスのキューにプッシュして、それらを順番に並べることができます...
もちろん、これは静的に型付けされた言語では不可能ですが (リフレクションを多用しない限り)、実際には非常に便利な機能です。プロキシ オブジェクト、コード ロード オン デマンド、リモート プロシージャ コール、学習および自己修正コード、適応および自己最適化プログラム、corba および dcom ラッパー、ワーカー キューはすべて、このスキームに基づいて構築されています。もちろん、誤用される可能性があり、実行時のバグにつながる可能性があります。だから二刀流です。鋭く力強いが、初心者が手にすると危ない…
編集: ここで言語の実装について書いています (Java と Smalltalk のように - プロセス間メカニズムではありません。
IIRC、それらは同等であることが正式に証明されています。少なくともそうあるべきであることを示すのに、それほど多くのことを考える必要はありません。呼び出されたアドレスとメモリ内の実際のスポットとの直接的な等価性をしばらくの間無視し、単純に数値と見なすだけで十分です。この観点からすると、番号は、呼び出したい特定のタイプの機能を一意に識別する単なる抽象的な識別子です。
同じマシンで関数を呼び出している場合でも、呼び出されたアドレスが呼び出された関数の物理 (または仮想) アドレスを直接指定するという実際の要件はありません。たとえば、実際に使用する人はほとんどいませんが、Intel プロテクト モードのタスク ゲートを使用すると、タスク ゲート自体を直接呼び出すことができます。この場合、アドレスのセグメント部分のみが実際のアドレスとして扱われます。つまり、タスク ゲート セグメントへの呼び出しは、指定されたオフセットに関係なく、同じアドレスを呼び出すことになります。必要に応じて、処理コードは指定されたオフセットを調べ、それを使用して呼び出す個々のメソッドを決定できますが、指定されたオフセットと呼び出された関数のアドレスとの関係は完全に任意にすることができます。
メンバー関数呼び出しは、問題のサービスのクライアントとサーバーが共通のアドレス空間を共有するという共通の状況下で最適化を提供する (または少なくとも容易にする) メッセージ パッシングの一種です。抽象サービス識別子とそのサービスのプロバイダーが存在するアドレスとの間の 1:1 対応により、一方から他方への単純で非常に高速なマッピングが可能になります。
同時に、それについて誤解しないでください: 何かがメンバー関数呼び出しのように見えるという事実は、別のマシンまたは非同期で、または (多くの場合) その両方で実際に実行することを妨げません。これを実現する典型的なメカニズムは、メンバー関数呼び出しの「仮想メッセージ」を、(たとえば) 必要に応じてネットワーク経由で送信できる「実際のメッセージ」に変換するプロキシ関数です (たとえば、Microsoft の DCOM と CORBA はどちらも送信します)。これは非常に日常的です)。
メッセージの例として Objective-C を、メソッドの例として Java を使用すると、主な違いは、メッセージを渡すときに、オブジェクトがそのメッセージの処理方法を決定することです (通常、オブジェクト内のインスタンス メソッドが呼び出されます)。
ただし、Java では、メソッド呼び出しはより静的なものです。これは、メソッドを呼び出している型のオブジェクトへの参照が必要であり、同じ名前と型シグネチャを持つメソッドがその型またはコンパイラに存在する必要があるためです。文句を言います。興味深いのは、実際の呼び出しが動的であることですが、これはプログラマには明らかではありません。
たとえば、次のようなクラスを考えてみましょう。
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 では、オブジェクトが理解できない可能性があると考えられるオブジェクトにメッセージを渡した場合、コンパイラは単に警告を発行しますが、それを無視してもプログラムの実行は停止しません。
これは非常に強力で柔軟性がありますが、スタックの破損が原因で誤って使用すると、見つけにくいバグが発生する可能性があります。
それらは実際には同じものではありません。メッセージ パッシングは、2 つ以上の並列プロセス間でデータと命令を転送する方法です。メソッド呼び出しは、サブルーチンを呼び出す方法です。Erlang の並行性は、以前の概念に基づいて並行指向プログラミングで構築されています。
メッセージの受け渡しにはメソッド呼び出しの形式が含まれる可能性が最も高いですが、メソッドの呼び出しには必ずしもメッセージの受け渡しが含まれるとは限りません。もしそうなら、それはメッセージパッシングです。メッセージ パッシングは、並列プロセス間で同期を実行する 1 つの形式です。メソッド呼び出しは、通常、同期アクティビティを意味します。呼び出し元は、メソッドが終了するのを待ってから続行します。メッセージパッシングは、コルーチンの一種です。メソッド呼び出しは、サブルーチンの形式です。
すべてのサブルーチンはコルーチンですが、すべてのコルーチンはサブルーチンではありません。
メッセージパッシングとメソッド呼び出しに違いはありますか、それとも同等と見なすことができますか?
それらは似ています。いくつかの違い:
メッセージは同期的または非同期的に渡すことができます(たとえば、WindowsのSendMessageとPostMessageの違い)
送信先のリモートオブジェクトを正確に知らなくても、メッセージを送信する可能性があります
ターゲットオブジェクトは、リモートマシンまたはO/S上にある可能性があります。