- PR -

Int32 を Decimal にキャストできません

投稿者投稿内容
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-05-28 21:19
コード:
Int32 i = 1;
Object o = i;
Decimal d = (Decimal)o;


最後の行でキャストできずに例外が発生します。

コード:
Integer i = 1;
Object o = i;
Number n = (Number)o;


Javaだと問題ありません。

.NET Framework で、このようなキャストができない原因を次のように考えました。.NET Framework では Java と異なり Int32 や Decimal はクラスではなく構造体である。構造体は継承ができないため Decimal が Int32 の親という関係にはなっていない。Int32 と Decimal が相互にキャストできるのはオペレータオーバーロードによるものなので、Object 型変数に格納されているとキャストできない。この認識はあっているでしょうか?

他にも疑問はあります。Object o = i; で問題がおこらないのはなぜでしょうか。構造体 Int32 が、なんらかのクラスに自動的にボクシングされるからではないのでしょうか。だとしたら、その Int32 構造体に対応するクラスは何でしょうか? それとも Object 型変数だけが特別な存在で構造体を含む値型を格納できる仕様なのでしょうか?

最後に Object 型変数を Decimal に変換する良い方法があれば教えてください。Object 型変数に格納されているのは Int32, Double など Decimal にキャスト可能な構造体です。Decimal にキャストできないものが Object 型変数に格納されていた場合は、キャスト例外が発生して構いません。if または switch で型別に分岐させるしかないでしょうか。
rain
ぬし
会議室デビュー日: 2006/10/19
投稿数: 549
投稿日時: 2008-05-28 21:58
未記入さんの疑問を全て解決するものではありませんが、
参考になると思いますので貼っておきます。
# 目次を辿ると、この他にもJava経験者向けの資料があるので役に立つと思います。
http://msdn.microsoft.com/ja-jp/library/ms228360(VS.80).aspx

最後の問いに関しては、Object型からの変換ということであれば、Convert.ToDecimal() メソッドが最も目的に叶うと思います。
まりも
ベテラン
会議室デビュー日: 2006/08/19
投稿数: 77
投稿日時: 2008-05-28 22:07
引用:

.NET Framework で、このようなキャストができない原因を次のように考えました。.NET Framework では Java と異なり Int32 や Decimal はクラスではなく構造体である。構造体は継承ができないため Decimal が Int32 の親という関係にはなっていない。Int32 と Decimal が相互にキャストできるのはオペレータオーバーロードによるものなので、Object 型変数に格納されているとキャストできない。この認識はあっているでしょうか?



そんな感じでいいと思います。

引用:

他にも疑問はあります。Object o = i; で問題がおこらないのはなぜでしょうか。



構造体は暗黙にValueTypeを継承しているようです。
そしてValueTypeはObjectを継承しています。

だから、クラスも構造体も、すべてObjectは継承しています。
なのでToString()などが使えるわけです。

コード:
Decimal d = Decimal.Parse(o.ToString())



一応これでできるみたいです。
ちょっと無駄な処理をやっていそうですが。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2008-05-28 22:10
とりあえず答えは、
Decimal d = (Int32)o;

Decimal d = (Decimal)(Int32)o;
です。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2008-05-29 01:22
引用:

とりあえず答えは、
Decimal d = (Int32)o;

Decimal d = (Decimal)(Int32)o;
です。



それだと小数部とか Int32 の範囲越えの時まずいんじゃ?

Decimal d = Convert.ToDecimal(o);

とか。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2008-05-29 10:40
引用:

それだと小数部とか Int32 の範囲越えの時まずいんじゃ?


もしかしたら、私は質問の意図を汲みとれてないかな…。
元が Int32 だから問題ないと思いますけど…。
rain
ぬし
会議室デビュー日: 2006/10/19
投稿数: 549
投稿日時: 2008-05-29 10:42
引用:

未記入さんの書き込み (2008-05-28 21:19) より:

最後に Object 型変数を Decimal に変換する良い方法があれば教えてください。Object 型変数に格納されているのは Int32, Double など Decimal にキャスト可能な構造体です。Decimal にキャストできないものが Object 型変数に格納されていた場合は、キャスト例外が発生して構いません。


とのことなので、実際にはInt32かどうかはわかりません。
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-05-29 10:50
回答ありがとうございます。

Convert.ToDecimal(Object value) を使用することで問題を解決することができました。パラメータの型が Object になっているので、もしかすると、内部で Decimal.Parse(value.ToString()) しているだけかと心配しましたが、リファレンスを読むとパラメータとして正しく処理されるのは IConvertible インターフェイスを実装した Object だけということなので、Object.ToString() に頼った変換ではなさそうです。

なんで、この ToDecimal(Object value) のシグネチャを ToDcimal(IConvertible value) にしなかったんでしょうね?

コード:
Decimal d = Decimal.Parse(o.ToString())


この方法は私も思いついていたのですが、Double.ToString() などが指数表現で文字列を返すことがあり、Decimal.ToParse() で復元できないため不適当と判断しました。

引用:

クラスも構造体も、すべてObjectは継承しています。なのでToString()などが使えるわけです。


たしかに。構造体もメソッドを持っていますね。構造体も Object を継承したクラスだとすると、Object 型変数に代入できるのもなんら不思議ではありませんね。rain さんに紹介していただいたページでは "Int32 クラス" という記述もありますし。.NET Framework では Int32 はプリミティブ型である。.NET Framework ではプリミティブ型もオブジェクトであるということのようですね。(Java ではプリミティブ型はオブジェクトではありません。)

これですっきりしたと思いきや。rain さんの紹介してくださったページ( http://msdn.microsoft.com/ja-jp/library/ms228360(VS.80).aspx )の最下部で「ボックス化とボックス化解除」という説明がありますが、うーむ、むずかしい。

コード:
int i = 123;     // a value type
object o = i;    // boxing
int j = (int)o;  // unboxing


int は System.Int32 のシノニム、object は System.Object のシノニムですよね。つまり、int は object を継承している。であれば、2行目の object o = i; という代入はできて当たり前ですよね。なぜ、これがボックス化と呼ばれるのでしょうか。

ボックス化変換( http://msdn.microsoft.com/ja-jp/library/25z57t8s(VS.80).aspx ) というページを読んで、次のように考えました。まず、Java と C# ではボックス化の意味がかなり異なっている。Java ではプリミティブ型それぞれに対応するラッパークラスに変換することをボックス化と呼ぶ。C# ではプリミティブ型それぞれに対応するラッパークラスが存在しているわけではない。上記の object o = i; でのボックス化とはプリミティブ型(構造体) i を Object 型に変換することを意味している。

なぜ、Object を継承している int を、ボックス化として Object に変換する必要があるのか理解できなかったのですが…。どうやら、プリミティブ型はオブジェクトであるにもかかわらず特別仕様によって、ヒープに配置されないようです。これをヒープに配置するために Object 型への変換(ボックス化)がおこなわれる。という認識でいいんでしょうか。

C# では int がオブジェクトだということは分かりましたが、.NET Framework は言語非依存なんですよね。C++ や J# の int も C# と同様にオブジェクトなのか? もし、C++ や J# の int がオブジェクトではなく、Java でいうところのプリミティブ型だとしたら、どうやってアセンブリの相互利用を実現しているのか? うーむ。.NET Framework むずかしいです。MSIL について勉強したら分かるようになるんでしょうかね。

スキルアップ/キャリアアップ(JOB@IT)