プロジェクトを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'の使用
この問題の原因は何ですか?コンパイラの設定で修正することはできますか?
この問題の原因は何ですか?
私にはコンパイラのバグのように見えます。少なくとも、そうでした。 decimal.TryParse(v, out a)
およびdecimal.TryParse(v, out b)
式は動的に評価されますが、Iexpectedコンパイラーは、a <= b
に到達するまでに、 a
とb
は必ず割り当てられます。動的型付けで思い付くような奇妙さでも、TryParse
呼び出しの両方を評価した後にのみa <= b
を評価することを期待しています。
ただし、演算子と変換が難しいため、A
とC
を評価するが、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標準で修正されることを確認しようとします。
これは、Roslynコンパイラーのバグ、または少なくとも回帰のようです。それを追跡するために、次のバグが報告されています。
それまでの間、Jonの excellent answer にはいくつかの回避策があります。
私はバグレポートで一生懸命勉強したので、これを自分で説明しようとします。
T
とbool
で始まるfalse
とtrue
の間で交互に行われるfalse
への暗黙的なキャストを持つdynamic
を想像してください。コンパイラーが知る限り、最初の&&
のT
最初の引数はその型に評価される可能性があるため、悲観的でなければなりません。
次に、コードをコンパイルすると、これが発生する可能性があります。
&&
を評価するとき、次のことを行います。bool
です-暗黙的にfalse
にキャストします。false
なので、2番目の引数を評価する必要はありません。&&
の結果を最初の引数として評価します。 (いいえ、T
ではなく、何らかの理由で。)&&
を評価するとき、以下を実行します。bool
です-暗黙的にtrue
にキャストします。b
なので、2番目の引数を評価します。false
は割り当てられていません。要するに、変数が「明確に割り当てられている」か「間違いなく割り当てられている」かだけでなく、「true
ステートメント」または「dynamic
ステートメントの後に確実に割り当てられます」。
これらは、&&
および||
(および!
および??
および?:
)を処理するときに、コンパイラが変数が特定のブランチに割り当てられるかどうかを調べることができるように存在します。複雑なブール式の。
ただし、これらは機能しますが、式の型はブール値のままです。式の一部がtrue
(または非ブール静的型)である場合、式がfalse
またはbool
であると確実に言うことはできなくなります-次回に_にキャストするとき[SOME_VARIABLE] _どのブランチを取るかを決定するために、考えが変わった可能性があります。
以前のコンパイラーが動的式用に実装した明確な割り当て規則により、コードの一部のケースでは、変数が正しく割り当てられずに読み取られる可能性がありました。この報告の1つについては、 https://github.com/dotnet/roslyn/issues/4509 を参照してください。
...
この可能性があるため、valに初期値がない場合、コンパイラはこのプログラムのコンパイルを許可してはなりません。以前のバージョンのコンパイラ(VS2015より前)では、valに初期値がない場合でもこのプログラムをコンパイルできました。 Roslynは現在、初期化されていない可能性のある変数を読み取ろうとするこの試行を診断します。
これはバグではありません。このフォームの動的な式がそのようなout変数を未割り当てのままにする方法の例については、 https://github.com/dotnet/roslyn/issues/4509#issuecomment-13087271 を参照してください。