42

次の C# ソース コードの抜粋があります。

object valueFromDatabase;
decimal result;
valueFromDatabase = DBNull.Value;

result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0);
result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);

最初の結果評価はスローしInvalidCastExceptionますが、2 番目の結果評価はスローしません。これら2つの違いは何ですか?

4

8 に答える 8

102

更新: この質問は、2010 年 5 月 27 日の私のブログの主題でした。素晴らしい質問をありがとう!

ここには非常に多くの非常に紛らわしい答えがあります。あなたの質問に正確に答えるようにしましょう。これを単純化しましょう:

object value = whatever;
bool condition = something;
decimal result = (decimal)(condition ? value : 0);

コンパイラは最後の行をどのように解釈しますか? コンパイラが直面する問題は、条件式の型が両方の分岐で一貫していなければならないということです。言語規則では、一方のブランチで object を返し、もう一方のブランチで int を返すことは許可されていません。選択肢は object と int です。すべての int はオブジェクトに変換できますが、すべてのオブジェクトが int に変換できるわけではないため、コンパイラはオブジェクトを選択します。したがって、これは

decimal result = (decimal)(condition ? (object)value : (object)0);

したがって、返されるゼロはボックス化された int です。

次に、int を 10 進数にボックス化解除します。ボックス化された int を 10 進数にアンボックスすることは違法です。その理由については、そのテーマに関する私のブログ記事を参照してください。

表象とアイデンティティ

基本的に、あなたの問題は、次のように、10 進数へのキャストが分散されているかのように振る舞っていることです。

decimal result = condition ? (decimal)value : (decimal)0;

しかし、これまで見てきたように、そうではありません

decimal result = (decimal)(condition ? value : 0);

意味。これは、「両方の選択肢をオブジェクトにしてから、結果のオブジェクトをアンボックスする」ことを意味します。

于 2009-07-23T15:51:31.403 に答える
12

Object違いは、コンパイラが と の間で適切に一致するデータ型を判断できないことInt32です。

int値を明示的にキャストしobjectて、2 番目と 3 番目のオペランドで同じデータ型を取得してコンパイルすることができますが、それはもちろん、値をボックス化してボックス化解除することを意味します。

result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0);

それはコンパイルされますが、実行されません。10 進数値としてボックス化解除するには、10 進数値をボックス化する必要があります。

result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M);
于 2009-07-23T13:37:50.237 に答える
5

演算子の型はオブジェクトになり、結果が 0 でなければならない場合は、暗黙的にボックス化されます。ただし、0リテラルはデフォルトでint型であるため、intをボックス化します。しかし、10 進数への明示的なキャストを使用すると、許可されていないボックス化を解除しようとします (ボックス化された型は、キャストバックする型とほぼ同じでなければなりません)。そのため、例外が発生する可能性があります。

以下は、C# 仕様からの抜粋です。

?: 演算子の 2 番目と 3 番目のオペランドは、条件式のタイプを制御します。X と Y を 2 番目と 3 番目のオペランドの型とします。それで、

  • X と Y が同じ型の場合、これは条件式の型です。
  • それ以外の場合、X から Y への暗黙の変換 (§6.1) が存在するが、Y から X への変換は存在しない場合、Y は条件式の型です。
  • それ以外の場合、Y から X への暗黙の変換 (§6.1) が存在するが、X から Y への変換は存在しない場合、X は条件式の型です。
  • そうしないと、式の型を判別できず、コンパイル エラーが発生します。
于 2009-07-23T13:38:25.587 に答える
4

あなたの行は次のようになります:

result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m;

0mはゼロの10進定数です

条件演算子の両方の部分は、同じデータ型に評価される必要があります

于 2009-07-23T13:42:00.293 に答える
3

x : y の部分には共通の型が必要です。データベースの値はおそらくある種の float であり、0 は int です。これは、10 進数へのキャストの前に発生します。": 0.0" または ": 0D" を試してください。

于 2009-07-23T13:37:59.703 に答える
2

私が間違っていない限り(これは非常に可能性が高いです)、実際には0が例外の原因です。これは、リテラルのタイプを想定した.NET(狂ったように)に依存するため、0だけでなく0mを指定する必要があります。

詳細については、 MSDNを参照してください。

于 2009-07-23T13:39:15.727 に答える
0

コンパイラが(コンパイル時に)どちらを10進数にキャストするかを決定するには、2つの異なるタイプがあります。これはできません。

于 2009-07-23T13:41:08.593 に答える
-1

両方を組み合わせると、答えがうまくいきます。

result = (decimal)(valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);

少なくとも、同様の状況がパラメーターにキャストされます。

于 2012-01-13T15:34:29.477 に答える