web-dev-qa-db-ja.com

ロズリンはコードのコンパイルに失敗しました

プロジェクトをVS2013からVS2015に移行した後、プロジェクトはビルドされなくなりました。次のLINQステートメントでコンパイルエラーが発生します。

static void Main(string[] args)
{
    decimal a, b;
    IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
    var result = (from v in array
                  where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
                  orderby decimal.Parse(v)
                  select v).ToArray();
}

コンパイラーはエラーを返します。

エラーCS0165未割り当てのローカル変数 'b'の使用

この問題の原因は何ですか?コンパイラの設定で修正することはできますか?

95
ramil89

この問題の原因は何ですか?

私にはコンパイラのバグのように見えます。少なくとも、そうでした。 decimal.TryParse(v, out a)およびdecimal.TryParse(v, out b)式は動的に評価されますが、Iexpectedコンパイラーは、a <= bに到達するまでに、 abは必ず割り当てられます。動的型付けで思い付くような奇妙さでも、TryParse呼び出しの両方を評価した後にのみa <= bを評価することを期待しています。

ただし、演​​算子と変換が難しいため、ACを評価するが、Bは評価しない式A && B && Cを使用することは完全に実行可能であることがわかります。 Neal Gafterの巧妙な例については、 Roslynバグレポート を参照してください。

dynamicでの動作をさらに難しくする-オペランドが動的である場合に関係するセマンティクスは、説明が難しくなります。オーバーロード解決を実行するには、オペランドを評価して、関係する型を調べる必要があるためです。直感に反する。ただし、Nealはコンパイラエラーが必要であることを示す例を示しました。これはバグではなく、バグfixです。それを証明してくれたニールへの多大な称賛。

コンパイラの設定で修正することはできますか?

いいえ。ただし、エラーを回避する代替手段があります。

まず、それが動的であることを止めることができます-文字列のみを使用することがわかっている場合は、IEnumerable<string>orを使用して範囲変数vを与えることができますstringのタイプ(つまりfrom string v in array)。それは私の好みのオプションです。

あなたが本当にそれを動的に保つ必要がある場合、bに開始値を与えるだけです:

decimal a, b = 0m;

これは何の害もありません-実際にあなたの動的な評価は何も狂わないことを知っているので、bに値を割り当てることになります使用する前に、初期値を無関係にします。

さらに、括弧の追加も機能しているようです:

where decimal.TryParse(v, out a) && (decimal.TryParse("15", out b) && a <= b)

これにより、オーバーロード解決のさまざまな部分がトリガーされるポイントが変更され、たまたまコンパイラーが幸せになります。

oneの問題がまだ残っています-&&演算子を使用した明確な割り当てに関する仕様のルールは、&&演算子が「通常の」 "2つのboolオペランドを使用した実装。これが次のECMA標準で修正されることを確認しようとします。

112
Jon Skeet

これは、Roslynコンパイラーのバグ、または少なくとも回帰のようです。それを追跡するために、次のバグが報告されています。

https://github.com/dotnet/roslyn/issues/4509

それまでの間、Jonの excellent answer にはいくつかの回避策があります。

21
JaredPar

私はバグレポートで一生懸命勉強したので、これを自分で説明しようとします。


Tboolで始まるfalsetrueの間で交互に行われるfalseへの暗黙的なキャストを持つdynamicを想像してください。コンパイラーが知る限り、最初の&&T最初の引数はその型に評価される可能性があるため、悲観的でなければなりません。

次に、コードをコンパイルすると、これが発生する可能性があります。

  • 動的バインダーが最初の&&を評価するとき、次のことを行います。
    • 最初の引数を評価する
    • これはboolです-暗黙的にfalseにキャストします。
    • ああ、それはfalseなので、2番目の引数を評価する必要はありません。
    • &&の結果を最初の引数として評価します。 (いいえ、Tではなく、何らかの理由で。)
  • 動的バインダーが2番目の&&を評価するとき、以下を実行します。
    • 最初の引数を評価します。
    • これはboolです-暗黙的にtrueにキャストします。
    • ああ、それはbなので、2番目の引数を評価します。
    • ...なんてこった、falseは割り当てられていません。

要するに、変数が「明確に割り当てられている」か「間違いなく割り当てられている」かだけでなく、「trueステートメント」または「dynamicステートメントの後に確実に割り当てられます」。

これらは、&&および||(および!および??および?:)を処理するときに、コンパイラが変数が特定のブランチに割り当てられるかどうかを調べることができるように存在します。複雑なブール式の。

ただし、これらは機能しますが、式の型はブール値のままです。式の一部がtrue(または非ブール静的型)である場合、式がfalseまたはboolであると確実に言うことはできなくなります-次回に_にキャストするとき[SOME_VARIABLE] _どのブランチを取るかを決定するために、考えが変わった可能性があります。


更新:これは 解決済み および 文書化済み になりました:

以前のコンパイラーが動的式用に実装した明確な割り当て規則により、コードの一部のケースでは、変数が正しく割り当てられずに読み取られる可能性がありました。この報告の1つについては、 https://github.com/dotnet/roslyn/issues/4509 を参照してください。

...

この可能性があるため、valに初期値がない場合、コンパイラはこのプログラムのコンパイルを許可してはなりません。以前のバージョンのコンパイラ(VS2015より前)では、valに初期値がない場合でもこのプログラムをコンパイルできました。 Roslynは現在、初期化されていない可能性のある変数を読み取ろうとするこの試行を診断します。

16
Rawling

これはバグではありません。このフォームの動的な式がそのようなout変数を未割り当てのままにする方法の例については、 https://github.com/dotnet/roslyn/issues/4509#issuecomment-13087271 を参照してください。

15
Neal Gafter