私は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つの違いは何ですか?
更新:この質問は 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);
手段。つまり、「両方の選択肢をオブジェクトにしてから、結果のオブジェクトを箱から出す」ということです。
違いは、コンパイラーがObject
とInt32
の間で適切に一致するデータ型を判別できないことです。
int
値をobject
に明示的にキャストして、2番目と3番目のオペランドで同じデータ型を取得してコンパイルすることができますが、それは、値をボックス化およびボックス化解除していることを意味します。
result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0);
コンパイルされますが、実行されません。 10進値としてボックス化解除するには、10進値をボックス化する必要があります。
result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M);
演算子のタイプはオブジェクトになり、結果が0でなければならない場合は、暗黙的にボックス化されます。ただし、0リテラルはデフォルトでint型であるため、intをボックス化します。ただし、10進数への明示的なキャストでは、許可されていないボックス化解除を試みます(ボックス化されたタイプは、キャストバックしたタイプと同じである必要があります)。そのため、例外が発生する可能性があります。
これはC#仕様からの抜粋です:
?:演算子の2番目と3番目のオペランドは、条件式のタイプを制御します。 XとYを2番目と3番目のオペランドのタイプとします。次に、
あなたの行は次のようになります:
result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m;
mはゼロの10進定数です
条件演算子の両方の部分は、同じデータ型に評価される必要があります
X:yの部分には共通の型が必要であり、データベースの値はある種のfloatである可能性が高く、0はintです。これは、10進数にキャストする前に発生します。 「:0.0」または「:0D」を試してください。
私が間違っていない限り(これは非常に可能性が高いです)、実際には0が例外の原因です。これは、リテラルのタイプを想定した.NET(狂ったように)に依存するため、0だけでなく0mを指定する必要があります。
詳細については、 [〜#〜] msdn [〜#〜] を参照してください。
コンパイラが(コンパイル時に)どちらを10進数にキャストするかを決定するための2つの異なるタイプがあります。これはできません。