トーマスの予想はかなり良いです。拡張メソッドに関する彼の推論は的確です。基本的に、拡張メソッドを機能させるには、実行時に呼び出しサイトで、コンパイル時に使用されているディレクティブが何であるかを知る必要があります。この情報をコールサイトに保持できるシステムを開発するための十分な時間や予算がなかっただけです。
ラムダの場合、実際には、ラムダが式ツリーに移動するのかデリゲートに移動するのかを判断するという単純な問題よりも状況が複雑になります。次のことを考慮してください。
d.M(123)
ここで、dは動的型の式です。*呼び出しサイト「M」への引数として実行時にどのオブジェクトを渡す必要がありますか?明らかに、123をボックス化して渡します。次に、ランタイムバインダーのオーバーロード解決アルゴリズムは、dのランタイムタイプとint 123のコンパイル時タイプを調べ、それを処理します。
さて、もしそれが
d.M(x=>x.Foo())
では、引数としてどのオブジェクトを渡す必要がありますか?「xのタイプが何であれ、Fooと呼ばれる未知の関数を呼び出す1つの変数のラムダメソッド」を表す方法はありません。
この機能を実装したいとします。何を実装する必要がありますか?まず、バインドされていないラムダを表す方法が必要です。式ツリーは、すべてのタイプとメソッドが既知であるラムダを表すためだけに設計されています。新しい種類の「型なし」式ツリーを発明する必要があります。次に、ランタイムバインダーにラムダバインディングのすべてのルールを実装する必要があります。
その最後の点を考慮してください。ラムダにはステートメントを含めることができます。 この機能を実装するには、ランタイムバインダーにC#で可能なすべてのステートメントのセマンティックアナライザー全体が含まれている必要があります。
それは私たちの予算から桁違いに大きかった。その機能を実装したいのであれば、今日もC#4に取り組んでいます。
残念ながら、これは、LINQが動的ではうまく機能しないことを意味します。これは、LINQはもちろん、あらゆる場所で型指定されていないラムダを使用するためです。うまくいけば、C#のいくつかの仮想的な将来のバージョンでは、より完全な機能を備えたランタイムバインダーと、バインドされていないラムダの同像表現を実行する機能があります。しかし、私があなただったら、私は息を止めませんでした。
更新:コメントは、セマンティックアナライザーについてのポイントについての説明を求めています。
次の過負荷を考慮してください。
class C {
public void M(Func<IDisposable, int> f) { ... }
public void M(Func<int, int> f) { ... }
...
}
と電話
d.M(x=> { using(x) { return 123; } });
dがコンパイル時タイプ動的およびランタイムタイプCであると仮定します。ランタイムバインダーは何をする必要がありますか?
ランタイムバインダーは、実行時に、式がMの各オーバーロードの各デリゲートタイプに変換可能かどうかを判断する必要がありますx=>{...}
。
これを行うには、ランタイムバインダーが2番目のオーバーロードが適用できないことを判別できる必要があります。該当する場合は、usingステートメントの引数としてintを使用できますが、usingステートメントの引数は使い捨てである必要があります。つまり、ランタイムバインダーは、usingステートメントのすべてのルールを認識し、usingステートメントの使用の可能性が合法か違法かを正しく報告できる必要があります。
明らかに、それはusingステートメントに限定されません。ランタイムバインダーは、特定のステートメントラムダが特定のデリゲート型に変換可能かどうかを判断するために、すべてのC#のすべてのルールを知っている必要があります。
本質的に、ILではなくDLRツリーを生成するまったく新しいC#コンパイラであるランタイムバインダーを作成する時間がありませんでした。ラムダを許可しないことで、メソッド呼び出し、算術式、およびその他のいくつかの単純な種類の呼び出しサイトをバインドする方法を知っているランタイムバインダーを作成するだけで済みます。ラムダを許可すると、ランタイムバインディングの問題が、実装、テスト、および保守に数十倍または数百倍のコストがかかります。