実際のエラーが報告されないのはなぜですか?
いいえ、それが問題です。実際のエラーを報告しています。
もう少し複雑な例で説明しましょう。これがあるとします:
class CustomerCollection
{
public IEnumerable<R> Select<R>(Func<Customer, R> projection) {...}
}
....
customers.Select( (Customer c)=>c.FristNmae );
OK、C# 仕様によると、エラーは何ですか? ここでは、仕様を注意深く読む必要があります。解決しましょう。
Select の呼び出しは、引数が 1 つあり、型引数がない関数呼び出しです。Select という名前の呼び出し可能なもの、つまりデリゲート型のフィールドやメソッドなどを検索して、CustomerCollection の Select を検索します。型引数が指定されていないため、任意のジェネリック メソッド Select で一致します。1 つを見つけて、そこからメソッド グループを作成します。メソッド グループには 1 つの要素が含まれます。
メソッドグループはオーバーロード解決によって分析され、最初に候補セットを決定し、次にそれから適用可能な候補セットを決定し、そこから最適な適用可能な候補を決定し、そこから最終的に検証された最適な適用可能な候補を決定する必要があります。これらの操作のいずれかが失敗した場合、オーバーロードの解決はエラーで失敗する必要があります。それらのどれが失敗しますか?
候補セットを構築することから始めます。候補を取得するには、メソッドの型推論を実行して、型引数 R の値を決定する必要があります。メソッドの型推論はどのように機能しますか?
パラメータの型がすべてわかっているラムダがあります。正式なパラメータは Customer です。R を決定するには、ラムダの戻り値の型から R へのマッピングを作成する必要があります。ラムダの戻り値の型は何ですか?
c が Customer であると仮定し、ラムダ本体の分析を試みます。これを行うと、Customer のコンテキストで FristNmae のルックアップが行われ、ルックアップは失敗します。
したがって、ラムダの戻り値の型の推論は失敗し、R に境界が追加されません。
すべての引数が分析された後、R に制限はありません。したがって、メソッドの型推論は R の型を決定できません。
したがって、メソッド型の推論は失敗します。
したがって、メソッドは候補セットに追加されません。
したがって、候補セットは空です。
したがって、該当する候補者はあり得ません。
したがって、ここでの正しいエラー メッセージは、「候補セットが空だったため、過負荷の解決で、最終的に検証された最適な候補を見つけることができませんでした」のようなものになります。
顧客は、このエラー メッセージに非常に不満を感じるでしょう。ユーザーが実際にエラーを修正するためのアクションを実行できる、より「根本的な」エラーを推測しようとするエラー報告アルゴリズムに、かなりの数のヒューリスティックを組み込みました。理由:
OK、「メソッド型の推論に失敗したため、オーバーロードの解決に失敗しました」というエラーを報告する必要がありますか? 繰り返しますが、顧客はそれを不満に思うでしょう。代わりに、「なぜメソッドの型推論が失敗したのか?」という質問をもう一度します。
これもひどいエラーです。境界が空に設定されたのはなぜですか?
- R を決定できる唯一の引数は、戻り値の型を推測できないラムダだったからです。
OK、「ラムダの戻り値の型の推論が戻り値の型の推論に失敗したため、オーバーロードの解決に失敗しました」というエラーを報告する必要がありますか? 繰り返しますが、顧客はそれを不満に思うでしょう。代わりに、「ラムダが戻り値の型を推測できなかったのはなぜですか?」という質問をします。
- Customer には FristNmae というメンバーがいないためです。
そして、それが私たちが実際に報告するエラーです。
したがって、必要なエラー メッセージを表示するために通過しなければならない推論の完全に曲がりくねった連鎖がわかります。過負荷解決に空の候補セットが与えられたということだけでは、何が問題だったかを言うことはできません。過負荷解決がどのようにしてその状態になったかを判断するには、過去を掘り下げる必要があります。
これを行うコードは非常に複雑です。これは、n 個の異なるジェネリック メソッドがあり、m 個の異なる理由で型推論が失敗する場合など、私が提示したものよりも複雑な状況を扱います。ユーザー。実際には、十数種類の選択とオーバーロードの解決があり、それらすべてがさまざまな理由または同じ理由で失敗する可能性があることを思い出してください。
コンパイラのエラー報告には、あらゆる種類のオーバーロード解決の失敗に対処するためのヒューリスティックがあります。私が説明したものは、それらの 1 つにすぎません。
それでは、あなたの特定のケースを見てみましょう。本当のエラーは何ですか?
Foo という 1 つのメソッドを含むメソッド グループがあります。候補セットを作成できますか?
はい。候補があります。メソッド Foo は、すべての必須パラメータ (bar) が指定されており、余分なパラメータがないため、呼び出しの候補です。
OK、候補セットにはメソッドが 1 つ含まれています。候補セットに該当するメンバーはありますか?
いいえ。ラムダ本体にエラーが含まれているため、bar に対応する引数を仮パラメータ型に変換できません。
したがって、適用可能な候補セットは空であり、最終的に検証された最適な適用可能な候補がないため、オーバーロードの解決は失敗します。
では、エラーは何でしょうか?繰り返しになりますが、「過負荷の解決は、最終的に検証された最適な候補を見つけることができなかった」とは言えません。顧客が私たちを嫌うからです。エラーメッセージを探し始める必要があります。オーバーロードの解決が失敗したのはなぜですか?
なぜ空だったのですか?
最適な候補はありましたか?
なぜ拒否されたのですか?
- その引数が仮パラメータ型に変換できなかったためです。
OK、この時点で、明らかに、名前付き引数を含むオーバーロード解決の問題を処理するヒューリスティックは、十分に掘り下げたので、これが報告すべきエラーであると判断します。名前付き引数がない場合、他のヒューリスティックが次のように尋ねます。
議論が転換できなかったのはなぜですか?
そして、そのエラーを報告します。
エラーヒューリスティックは完全ではありません。それからはほど遠い。偶然にも、私は今週、「単純な」オーバーロード解決エラー レポート ヒューリスティックの大幅な再構築を行っています。「2 つのパラメーターを受け取るメソッドはありませんでした」と言うときや、「必要なメソッドはプライベートです」と言うときなどです。 」、「その名前に対応するパラメーターはありません」と言う場合など。2 つの引数を持つメソッドを呼び出している可能性は十分にあります。2 つのパラメーターを持つその名前のパブリック メソッドはありません。1 つはプライベートですが、そのうちの 1 つは一致しない名前付き引数を持っています。クイック、どのエラーを報告する必要がありますか? 私たちは最善の推測をしなければなりません.時には、私たちが行うことができたかもしれないが、行うのに十分なほど洗練されていないより良い推測がある場合があります.
それを正しく行うことでさえ、非常にトリッキーな仕事であることが証明されています. 最終的に、LINQ 式内のメソッド型推論の失敗に対処する方法など、非常に負荷の高いヒューリスティックを再設計する段階になったら、あなたのケースを再検討し、ヒューリスティックを改善できるかどうかを確認します。
しかし、表示されるエラー メッセージは完全に正しいため、これはコンパイラのバグではありません。むしろ、特定のケースでのエラー報告ヒューリスティックの欠点にすぎません。