2

次の簡単なプログラムを考えてみましょう:

static class Program
{
  static void Main()
  {
  }

  static void Method(short? x)
  {
    const int y = 50; // note: is Int32, but is const and within Int16 range
    var z = x ?? y;   // note: var keyword used; IDE is confused about the type!
    TakeOnlyInt16(z);
    z.OnThisInt16();
  }

  static void TakeOnlyInt16(short a)
  {
  }
  static void OnThisInt16(this short a)
  {
  }
}

このプログラムにはまったく問題はなく、問題なくコンパイルされます。Method(そして、おそらくfromへの呼び出しを含めて実行できますMain。)

ただし、Visual Studio IDEは、ローカル変数の型について間違った印象を持っていますz。実際には( C# では別名) であるzのに、 であると考えているようです。この問題は、少なくとも 3 つの状況で発生します。Int32Int16short

  1. varキーワードに「ホバー」する (マウスを押したままにする)Int32と、ツールチップに表示されます。それは間違いです。

  2. テキスト (キーボード) カーソルをステートメントTakeOnlyInt16(z);inside内に移動するMethodと、このステートメントの左下隅に小さなヒントが表示され、「 in のメソッド スタブを生成する」ように提案されTakeOnlyInt16ますProgram。メソッドが明らかに既にそこにあるため、それは間違っています。しかし、すでに存在するオーバーロードは間違っていると考えているようです。shortint

  3. z.の中に (zed dot)と入力するMethodと、 のメンバーがintellisenseInt32で表示されます。のオーバーロードは、 によって宣言されたものではなく、によって宣言されたものであることに注意してください。また、入力すると、拡張メソッドが IntelliSense メンバー リストに表示されません。CompareToInt32Int16z.

スクリーンショットなしで私の説明を理解していただければ幸いです。

質問:このバグはどこから来たのですか? それはよく知られていますか?古いバージョンの Visual Studio にもありますか?

私はVS2013でこれを試しました。

4

3 に答える 3

2

C# リファレンス によると、null 合体演算子 (??)

null 許容値型または参照型の既定値を定義するために使用されます。オペランドが null でない場合は左側のオペランドを返します。それ以外の場合は、右側のオペランドを返します。

右側のオペランドがintで、null でない左側のオペランドが の場合short、コンパイラは と の間で選択する必要がintありshortます。そして、short暗黙的に変換できるようにint(逆はできません)、コンパイラはこの式の結果がint型であると正しく判断します。

まだ納得できませんか?なぜ逆にできなかったのでしょうか。うーん、 C# 言語仕様7.13の内容を見てみましょう。

式の型 a ?? b は、オペランドで使用できる暗黙の変換によって異なります。?? のタイプは優先順で b は A0、A、または B です。ここで、A は a の型 (a に型がある場合)、B は b の型 (b に型がある場合)、A の場合、A0 は A の基になる型です。 null 許容型、またはそれ以外の A です。

「利用可能な暗黙の変換」部分を無視したい場合は、A0 (短い) である必要があると思われる可能性があるため、仕様を読み続けましょう。

具体的には、?? b は次のように処理されます。

• A が存在し、null 許容型または参照型でない場合、コンパイル時エラーが発生します。

•b が動的式の場合、結果の型は動的です。実行時に、 a が最初に評価されます。a が null でない場合、a は動的に変換され、これが結果になります。それ以外の場合、b が評価され、これが結果になります。

•それ以外の場合、A が存在し、null 許容型であり、b から A0 への暗黙的な変換が存在する場合、結果の型は A0 になります。実行時に、 a が最初に評価されます。a が null でない場合、a は型 A0 にアンラップされ、これが結果になります。それ以外の場合、b が評価され、型 A0 に変換され、これが結果になります。 注: これは当てはまりません。b (int) から A0 (short) への変換はありません。

•それ以外の場合、A が存在し、b から A への暗黙的な変換が存在する場合、結果の型は A になります。実行時に、a が最初に評価されます。a が null でない場合、a が結果になります。それ以外の場合、b が評価されて型 A に変換され、これが結果になります。

•それ以外の場合、b の型が B で、a から B への暗黙的な変換が存在する場合、結果の型は B になります。実行時に、a が最初に評価されます。a が null でない場合、a は型 A0 (A が存在し、null 許容の場合) にアンラップされ、型 B に変換され、これが結果になります。それ以外の場合、b が評価され、結果になります。 注:これは事実です

•それ以外の場合、a と b は互換性がなく、コンパイル エラーが発生します。

いいえ、コンパイラにバグはありません。あなたは間違った仮定をしています。

于 2013-11-26T17:32:05.103 に答える