web-dev-qa-db-ja.com

C#?:条件演算子

私はC#2.0ソースコードのこの抜粋を持っています:

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つの違いは何ですか?

42
Ioannis

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

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

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

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

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

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

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

表現とアイデンティティ

基本的に、問題は、次のように、10進数へのキャストが分散されているかのように動作していることです。

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

しかし、私たちが見てきたように、それは何ではありません

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

手段。つまり、「両方の選択肢をオブジェクトにしてから、結果のオブジェクトを箱から出す」ということです。

102
Eric Lippert

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

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

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

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

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

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

これはC#仕様からの抜粋です:

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

  • XとYが同じ型の場合、これは条件式の型です。
  • それ以外の場合、暗黙の変換(§6.1)がXからYに存在するが、YからXには存在しない場合、Yは条件式のタイプです。
  • それ以外の場合、YからXへの暗黙の変換(§6.1)が存在するが、XからYへの変換は存在しない場合、Xは条件式のタイプです。
  • そうしないと、式の型を判別できず、コンパイル時エラーが発生します。
5
Dzmitry Huba

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

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

mはゼロの10進定数です

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

4

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

3
Donald Byrd

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

詳細については、 [〜#〜] msdn [〜#〜] を参照してください。

2
Mark

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

0
Ioannis