web-dev-qa-db-ja.com

intを10進数としてunboxできないのはなぜですか?

次のように、10進数を取得する_IDataRecord reader_があります。

_decimal d = (decimal)reader[0];
_

何らかの理由で、これにより、「指定されたキャストが無効です」という無効なキャスト例外がスローされます。

reader[0].GetType()を実行すると、それがInt32であることがわかります。私の知る限り、これは問題にはなりません...

私はこれを問題なく動作するこのスニペットでテストしました。

_int i = 3750;
decimal d = (decimal)i;
_

これにより、リーダーに含まれているintを10進数としてunboxできないのはなぜかと思って頭を悩ませました。

なぜこれが起こっているのか誰か知っていますか?私が見逃している微妙なものはありますか?

60
mezoid

値の型は、元の型(およびその型のnull許容バージョン)にのみボックス化解除できます。

ちなみに、これは有効です(2行バージョンの省略形です)。

object i = 4;
decimal d = (decimal)(int)i; // works even w/o decimal as it's a widening conversion

これの背後にある理由のためにこれを読んでください Eric Lippertのブログエントリ:表現とアイデンティティ

個人的に、私はキャスト構文によって行われたことを4つの異なるタイプの操作に分類します(すべて異なるIL命令を持っています):

  1. ボックス化(box IL命令)およびボックス化解除(unbox IL命令)
  2. 継承階層のキャスト(C++のdynamic_cast<Type>のように、castclass IL命令を使用して確認)
  3. プリミティブ型間のキャスト(C++のstatic_cast<Type>のように、プリミティブ型間のさまざまな型のキャストに対するIL命令がたくさんあります)
  4. ユーザー定義の変換演算子の呼び出し(ILレベルでは、それらは適切なop_XXXメソッドへの単なるメソッド呼び出しです)。
79
Mehrdad Afshari

これが簡単な解決策です。ボックス化を解除し、10進数にキャストします。私にとってはうまくいきました。

decimal d = Convert.ToDecimal(reader[0]);  // reader[0] is int
15
Sagar

intdecimalにキャストしても問題はありませんが、オブジェクトのボックス化を解除するときは、オブジェクトに含まれている正確な型を使用する必要があります。

int値をdecimal値に展開するには、まずintとして展開し、次に10進数にキャストします。

decimal d = (decimal)(int)reader[0];

IDataRecordインターフェイスには、値をボックス化解除するためのメソッドもあります。

decimal d = (decimal)reader.GetInt32(0);
15
Guffa

Mehrdad Afshari氏は次のように述べています。

値の型は、元の型(およびその型のnull許容バージョン)にのみボックス化解除できます。

キャストとボックス化解除には違いがありますです。 jerryjvlは素晴らしい発言をしました

ある意味で、ボックス化解除とキャストは構文的にまったく同じに見えるのは残念です。

鋳造:

int i = 3750; // Declares a normal int
decimal d = (decimal)i; // Casts an int into a decimal > OK

ボクシング/アンボクシング:

object i = 3750; // Boxes an int ("3750" is similar to "(int)3750")
decimal d = (decimal)i; // Unboxes the boxed int into a decimal > KO, can only unbox it into a int or int?
3
user276648