「委任」system.action 'は0の引数を取得しません。これはC#コンパイラバグ(Lambdas + 2つのプロジェクト)ですか?
-
11-10-2019 - |
質問
以下のコードを検討してください。完全に有効なC#コードのように見えますか?
//Project B
using System;
public delegate void ActionSurrogate(Action addEvent);
//public delegate void ActionSurrogate2();
// Using ActionSurrogate2 instead of System.Action results in the same error
// Using a dummy parameter (Action<double, int>) results in the same error
// Project A
public static class Class1 {
public static void ThisWontCompile() {
ActionSurrogate b = (a) =>
{
a(); // Error given here
};
}
}
コンパイラエラーの「デリゲート」アクションが取得されます 'は0の引数を取得しません。 (Microsoft)C#4.0コンパイラを使用した指定された位置で。このエラーがマニフェストするために、別のプロジェクトでActionSurogateを宣言する必要があることに注意してください。
もっと面白くなります:
// Project A, File 1
public static class Class1 {
public static void ThisWontCompile() {
ActionSurrogate b = (a) => { a(); /* Error given here */ };
ActionSurrogate c = (a) => { a(); /* Error given here too */ };
Action d = () => { };
ActionSurrogate c = (a) => { a(); /* No error is given here */ };
}
}
ここでC#コンパイラのバグに出くわしましたか?
これは、Lambdasを大いに使用するのが好きで、将来の使用のためにデータ構造ライブラリを作成しようとしている人にとってはかなり迷惑なバグです...(私)
編集:誤ったケースを削除しました。
これを実現するために、元のプロジェクトを最小限に抑えました。これは文字通り私の新しいプロジェクトのすべてのコードです。
解決
これはおそらくタイプの推測の問題であり、コンパイラが魅力的に推測するのは問題です a
として Action<T>
それ以外の Action
(それは思うかもしれません a
は ActionSurrogate
, 、それに合うでしょう Action<Action>>
サイン)。タイプを指定してみてください a
明示的に:
ActionSurrogate b = (Action a) =>
{
a();
};
そうでない場合 - 自己定義されたプロジェクトの周りでチェックするかもしれません Action
1つのパラメーターを取得する代表者。
他のヒント
最終更新:
バグはC#5で修正されています。ご不便をおかけし、再びお詫び申し上げます。レポートをありがとう。
オリジナル分析:
コマンドラインコンパイラで問題を再現できます。確かにバグのように見えます。それはおそらく私のせいです。申し訳ありません。 (私はすべてのLambdaからDelegateへの変換チェックコードを書きました。)
私は今コーヒーショップにいますが、ここからコンパイラソースにアクセスできません。明日デバッグビルドでこれを再現する時間を見つけて、何が起こっているのかを解決できるかどうかを確認しようと思います。時間が見つからない場合は、クリスマスの後までオフィスを出ます。
タイプのアクションの変数を導入すると、問題が消滅するというあなたの観察は非常に興味深いものです。コンパイラは、パフォーマンス上の理由と言語仕様で必要な分析の両方のために、多くのキャッシュを維持しています。特にラムダとローカル変数には、複雑なキャッシュロジックがたくさんあります。私は、ここでいくつかのキャッシュが初期化または間違っていること、そしてローカル変数の使用がキャッシュの正しい値に記入されることをドルに賭けたいと思っています。
レポートをありがとう!
更新:私は今バスに乗っていて、それはちょうど私のところに来ました。何が悪いのかを正確に知っていると思います。コンパイラはそうです 怠惰, 、特にメタデータから来た種類を扱う場合。その理由は、参照されているアセンブリに何十万ものタイプがある可能性があり、それらすべてに関する情報をロードする必要がないためです。あなたはおそらくそれらの1%未満をはるかに下回るので、あなたが決して使用しないものを多くの時間とメモリロードするものを無駄にしないでください。実際、怠lazはそれよりも深くなります。使用する前に、いくつかの「ステージ」を通過します。最初にその名前が知られている、次にそのベースタイプ、次にそのベースタイプの階層が十分に根拠があるか(非環式など)、そのタイプパラメーターの制約、次にメンバー、次にメンバーがよく根拠があるかどうか(オーバーライドは何かをオーバーライドするかどうか同じ署名など。)変換ロジックは、「すべてのデリゲートパラメーターのタイプがあることを確認してください。 彼らの 知られているメンバーは「互換性のためにデリゲートの署名が呼び出される前に知られています。しかし、おそらくローカル変数を作るコードはおそらく します それを行う。コンバージョンチェック中、アクションタイプには、コンパイラに関する限り、呼び出し方法さえ持たない場合があると思います。
まもなくわかります。
更新:私の精神的な力は今朝強いです。過負荷解像度が、引数がゼロになるデリゲートタイプの「呼び出し」方法があるかどうかを判断しようとすると、それは見つかります ゼロは、選択する方法を呼び出します. 。過負荷解像度を行う前に、デリゲート型メタデータが完全にロードされるようにする必要があります。これがこれほど長く気付かれなかったことがどれほど奇妙ですか。 C#3.0で再現されています。もちろん、ラムダがなかったという理由だけで、C#2.0ではreproしていません。 C#2.0の匿名の方法では、タイプを明示的に述べる必要があります。これにより、メタデータがロードされることがわかっているローカルが作成されます。しかし、バグの根本原因 - 過負荷解像度が呼び出しのメタデータを強制しない - はC#1.0に戻ると想像します。
とにかく、魅力的なバグ、レポートをありがとう。明らかに、あなたは回避策を持っています。ここからQAを追跡してもらい、C#5のために修正しようとします(ウィンドウを見逃しました。 すでにベータ版にあるサービスパック1.)
public static void ThisWontCompile()
{
ActionSurrogate b = (Action a) =>
{
a();
};
}
これはコンパイルされます。パラメーターなしでアクションデリゲートを見つけることができないコンパイラとのグリッチ。そのため、エラーが発生しています。
public delegate void Action();
public delegate void Action<T>();
public delegate void Action<T1,T2>();
public delegate void Action<T1,T2,T3>();
public delegate void Action<T1,T2,T3,T4>();