- PR -

キャストについて

投稿者投稿内容
未記入
大ベテラン
会議室デビュー日: 2006/05/19
投稿数: 125
投稿日時: 2009-01-07 13:52
キャストには
Ctype、Cint(CStrなど)、DirectCast、Parse、Convertなど色々ありますが
明確な使い分けがわかりません。

現状は
DirectCastはCtypeに比べて速いということで積極的に使用していますが
DirectCastが使用できない場合などはCtypeやCintなどのCXxxで済ませてしまいます。

明確な使い分けをご教示ください。


また下記についても、具体的にどのキャストを使用すべきなのかご教示お願いいたします。

1、テキストボックスに入力されている数字(カンマ区切りの数字文字列)を数値にキャストする場合はどのキャストを使うのでしょうか?
現状はCintを使用しています。

2、型なしデータテーブルのフィールド値(Object型。DBのフィールドはNumberで数値またはNull)の場合はどのキャストを使うのでしょうか?
現状はCintを使用しています。

※追記
あるコードで、型なしデータテーブルのフィールド値のキャストを
Convert.ToInt32(DataTable1.Rows(0).Item(0).ToString)
としていました。
この場合、フィールド値がNullであっても0と変換できるので、
Nullチェックしなくて便利だなと思ったのですが、なんとなく違和感を感じて
今回のキャストの使い分けを疑問に思った次第です。


よろしくお願いいたします。

テッテ
ベテラン
会議室デビュー日: 2008/03/16
投稿数: 91
投稿日時: 2009-01-07 16:47
MSDNはご覧になりましたか?

http://msdn.microsoft.com/ja-jp/library/kca3w8x6(VS.80).aspx
http://msdn.microsoft.com/ja-jp/library/k1e94s7e(VS.80).aspx
http://msdn.microsoft.com/ja-jp/library/7k6y2h6x(VS.80).aspx
http://msdn.microsoft.com/ja-jp/library/zyy863x8(VS.80).aspx

この辺りが参考になると思います。

明確な使い分けと言われると、正直わかりません。DirectCast や TryCast が使える変換はそれを使うのがいいと思いますが、使えない場合は、どういう変換をしたいのかを考えて、それにより適切な手段を取ります。ケース・バイ・ケースですのでなんとも言えませんし、好みもあると思います。

以下の回答も一意見に過ぎないと思ってください。

引用:
1. テキストボックスに入力されている数字(カンマ区切りの数字文字列)を数値にキャストする場合はどのキャストを使うのでしょうか?



数値型(Integer など)の Parse メソッドや、TryParse メソッドを使用します。

引用:
2、型なしデータテーブルのフィールド値(Object型。DBのフィールドはNumberで数値またはNull)の場合はどのキャストを使うのでしょうか?



中身は数値型か DBNull とわかっているケースなので、DBNull を除外すれば DirectCast が使用できる変換です。そこでまず Convert.IsDBNull で DBNull かどうかをチェックし、Null でないことがわかったら対応する数値型に DirectCast します。



少し本題とずれた話かも知れませんが、個人的には .NET 以降の VB では VB 固有の関数は極力使わないようにしています。ほとんどは .NET Framework に対応するクラス・メソッドが用意されていますし、大抵そちらの方が高機能だからです。

例えば Parse, TryParse についても、NumberStyles を指定して解釈の方法を制御することもできますので、CInt よりも高機能です。

http://www.atmarkit.co.jp/fdotnet/dotnettips/001atoi/atoi.html

また、VB しか使わないなら問題にならないかも知れませんが、C# では VB の関数は(基本的に)使わないので、こういうプログラミングスタイルに慣れておくと C# にも移行しやすいと思います。

そういう考えで、CInt、CStr のたぐいは私は全く使いません。DirectCast や CType はこれしかないので使いますが…
かるあ
ぬし
会議室デビュー日: 2003/11/16
投稿数: 1190
お住まい・勤務地: センガワ→ムサシノ
投稿日時: 2009-01-08 00:27
たしか、かなり前に ひろえむ さんのところでこんなエントリがありましたよ。
参考になれば
http://blogs.wankuma.com/hirom/archive/2007/03/08/65658.aspx
_________________
かるあ のメモスニペット
未記入
大ベテラン
会議室デビュー日: 2006/05/19
投稿数: 125
投稿日時: 2009-01-08 12:31
テッテさん、かるあさん ありがとうございます。
リンク先はすべて読んだ事があるのですが、どう使い分けるのかがイマイチわからなかったので質問しました。

再度リンク先を読んできて自分なりにまとめてみました。
Parseでは全角文字を変換できませんでした。
Convertでは全角文字、書式付文字列の変換ができませんでした。
(変換できる方法がありましたらご教示ください。)


私もVB関数はできる限り使用していません。
しかしテキストボックスに全角数字を入力しても数値にキャストしなければならない事や
テキストボックスに桁区切り付きの数字を入力しても数値にキャストしなければならない場面が多く
ParseやConvertでは面倒なのでCtypeまたはCintを使用しています。
※私の認識ではCtype("123",Integer)とCint("123")は等価だと思っております。


またhttp://msdn.microsoft.com/ja-jp/library/8s682k58(VS.80).aspxから
以下のコードが等価のような印象をうけました。

C#
double MyDouble = 123456789;
int MyInt = checked ((int)MyDouble);

VB
Dim MyDouble As Double = 123456789
Dim MyInt As Integer = CType(MyDouble, Integer)

C#で(int)MyDoubleは全角でも桁区切り付きでも数値にキャストできるのでしょうか?
(VB使いであるためわかりません。すいません。)


また型なしデータテーブルのフィールド値(Object型。DBのフィールドはNumberで数値またはNull)の場合はどのキャストを使うかですが
DirectCastではDecimalにしかキャストできませんでした。(Oracleです。後だしすみません)
以下どれでもキャスト可能ですが
Integer.Parse(DataTable1.Rows(0).Item(0).ToString)
Convert.ToInt32(DataTable1.Rows(0).Item(0).ToString)
Cint(DataTable1.Rows(0).Item(0))
Ctype(DataTable1.Rows(0).Item(0),Integer)
個人的には
Nullを0として扱うならConvert.ToInt32(DataTable1.Rows(0).Item(0).ToString) を使用すると思います。
NullチェックをしなければならいないならCint(DataTable1.Rows(0).Item(0))を使用すると思います。

色々試した結果
キャストに関しては、VB関数であるCintを使用しても仕方ないかなぁと思いました。



■Parse
---------------------------------------------------
文字列→基本データ型へのキャスト。
書式文字列からの変換ができる。
Nothingは変換できない。

Dim str As String

str = "1234567"
Dim intValue1 As Integer = Convert.ToInt32(str)
Console.WriteLine(intValue1.ToString)

''全角文字列は例外
'str = "1234567"
'Dim intValue2 As Integer = Convert.ToInt32(str)
'Console.WriteLine(intValue2.ToString)

str = "1,234,567"
Dim intValue3 As Integer = Integer.Parse(str, Globalization.NumberStyles.Any)
Console.WriteLine(intValue3.ToString)

str = "1,234,567.00"
Dim intValue4 As Integer = Integer.Parse(str, Globalization.NumberStyles.Any)
Console.WriteLine(intValue4.ToString)

''Nothingは例外
'str = Nothing
'Dim intValue5 As Integer = Integer.Parse(str)
'Console.WriteLine(intValue5.ToString)

str = "2009年1月9日"
Dim dtValue As Date = Date.ParseExact(str, "yyyy年M月d日", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None)
Console.WriteLine(dtValue.ToString)


■Convert.ToXxxx
---------------------------------------------------
基本データ型→基本データ型へのキャスト。
書式文字列からの変換ができない?
Nothingが変換できる。

Dim str As String

str = "1234567"
Dim intValue1 As Integer = Convert.ToInt32(str)
Console.WriteLine(intValue1.ToString)

''全角文字列は例外
'str = "1234567"
'Dim intValue2 As Integer = Convert.ToInt32(str)
'Console.WriteLine(intValue2.ToString)

''3桁区切文字列は例外
'str = "1,234,567"
'Dim intValue3 As Integer = Convert.ToInt32(str)
'Console.WriteLine(intValue3.ToString)

''小数がある文字列は例外
'str = "1234567.00"
'Dim intValue4 As Integer = Convert.ToInt32(str)
'Console.WriteLine(intValue4.ToString)

str = Nothing
Dim intValue5 As Integer = Convert.ToInt32(str)
Console.WriteLine(intValue5.ToString)

str = "2009年1月9日"
Dim dtValue As DateTime = Convert.ToDateTime(str)
Console.WriteLine(dtValue.ToString)


■Ctype(CInt,CBoolなどCXxx)
---------------------------------------------------
Dim str As String

str = "1234567"
Dim intValue1 As Integer = CType(str, Integer)
Console.WriteLine(intValue1.ToString)

str = "1234567"
Dim intValue2 As Integer = CType(str, Integer)
Console.WriteLine(intValue2.ToString)

str = "1,234,567"
Dim intValue3 As Integer = CType(str, Integer)
Console.WriteLine(intValue3.ToString)

str = "1,234,567.00"
Dim intValue4 As Integer = CType(str, Integer)
Console.WriteLine(intValue4.ToString)

str = Nothing
Dim intValue5 As Integer = CType(str, Integer)
Console.WriteLine(intValue5.ToString)

str = "2009年1月9日"
Dim dtValue As Date = CType(str, Date)
Console.WriteLine(dtValue.ToString)

テッテ
ベテラン
会議室デビュー日: 2008/03/16
投稿数: 91
投稿日時: 2009-01-08 16:44
引用:


Parseでは全角文字を変換できませんでした。




なるほど、おっしゃる通りのようです。私は全角数字を数値に変換するプログラムを作る必要に迫られたことがありませんが、必要となると結構面倒かも知れませんね。

引用:


C#で(int)MyDoubleは全角でも桁区切り付きでも数値にキャストできるのでしょうか?




これは何か質問がおかしいです。MyDouble は Double 型なのですから数値しかもっていません。桁区切りとか全角というのは、それを文字列として表示するときのお話ですよね。

引用:


DirectCastではDecimalにしかキャストできませんでした。(Oracleです。後だしすみません)




これは Oracle の NUMBER 型に対応する型が Decimal だからで、VB というよりは Oracle と VB の間の事情だと思います。

<参考>
http://msdn.microsoft.com/ja-jp/library/yk72thhd.aspx

引用:


Nullを0として扱うならConvert.ToInt32(DataTable1.Rows(0).Item(0).ToString) を使用すると思います。




TextBox の文字列の解析に CInt を使う方は許容できなくもありませんが、このコードは個人的には許容できません。

Fill 等で DataTable にデータが格納された時点で、(Object 型にラップされているものの)実体は Decimal 型なのですから、このコードだと、Decimal を ToString で文字列に変換して、その後再度、数値に変換しています。実験したわけではありませんが、たぶんパフォーマンスは良くないと思います。

Oracle の型の事情で Decimal にしかキャストできないとしても、私なら DirectCast でいったん Decimal にしてから CType あたりで Integer にすると思います。



ちょっと余談ですが、「文字列→基本データ型へのキャスト。」というような表現に違和感を覚えてしまいます。「文字列→基本データ型への変換。」なら違和感がないのですが。

私はこれはキャストではないと思っています。少なくともC# のキャスト演算子ではこれに相当する変換しか行うことができません。

例えば、C# で以下のコードは正常に動作します。

Double a = 12.3;
Int32 b = (int)a; // Double → Int32 VB なら CType

Object c = 12;
Int32 d = (int)c; // Object → Int32 VB なら DirectCast 可

しかし、以下のコードはコンパイルエラーになります。

String a = "12";
Int32 b = (int)a;

[型 'string' を型 'int' に変換できません。]

これは string から int に変換する方法をコンパイラが知らないからです。つまり、String 型に int への変換の方法が定義されていないからです。キャストというのは、変換方法が定義されている場合の型変換を言うのだと思っています。

CInt で行われる「それぞれの文字を見たまま数字として解釈して、文字列を10進数として解釈する。」という処理はキャストというよりは変換だと思います。日本語だとわかりにくいのですが、英語でいうなら Cast と Convert の違いなのかなとも思います。

そして、VB の CType は Cast だけでなく Convert までやってしまうことがあります。とはいっても、キャストにはやはり CType を使うしかないし…そこが VB のあまり好きではないところです…
未記入
大ベテラン
会議室デビュー日: 2006/05/19
投稿数: 125
投稿日時: 2009-01-09 12:36
テッテさん
丁寧なご指導ありがとうございます。

後輩にはVB関数を使わないよう指導しているのですが
Ctype,Cint,DirectCastは仕方ないので使ってもよいとしなければならないことが
もどかしいです。

例えば
文字列から数値に変換する場合はParseを使うが、
全角文字列が入る場合はCintを使用するというのは混乱すると思うのです。
とりあえずCintまたはCtypeでと指導すると思います。

DBのフィールド値(Number型)を変換するのに
Cint(DirectCast(Field1,Double))と指導するのも混乱するのではと思うのです。
NullチェックをしてからCintでと指導すると思います。

ただConvert.ToInt32(DataTable1.Rows(0).Item(0).ToString) と書かれても
強く否定はできません・・・。
個人的には積極的な使用はしないようにします。

VBではきっちり書きたくても書けないのですね・・・。


※同様にSystem.Environment.NewLineをvbCrlfと書かれても仕方ないとしています。
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2009-01-09 13:45
キャストという言葉は言語によって、人によって微妙に定義にブレがありますので、
例示によってある程度、キャストと型変換を明確にしておきたいと思います。

例1) Object型の変数に入っているDouble型の値を○○○してDouble型の変数に入れる。
例2) Int32型の変数に入っているInt32型の値を○○○してDouble型の変数に入れる。
例3) Double型の変数に入っているDouble型の値を○○○してInt32型の変数に入れる。
例4) String型の変数に入っているString型の値"123"を○○○してInt32型の変数に123を入れる。
例5) String型の変数に入っているString型の値"123"(全角)を○○○してInt32型の変数に123を入れる。

上記の例1〜例4の○○○などの伏せたところには処理の種類(型変換なのかキャストなのかなど)が入ります。

C#の場合。 (参考:キャストと型変換 (C# プログラミング ガイド)

例1、例3は明示的な型変換(キャスト)といいます。
(例3については、丸め方に応じた方法で変換するべきでしょうけど)
例2は暗黙的な型変換(こちらも明示的にもできるので、キャストの一種)と言いいます。
例4はInt32.Parseなどを使って処理します。例5は独自に実装して処理します。
キャストと呼べるのは例1、2、3のみです。例4、例5も型変換とは言ってもいいような気がします。
また、これら全体を指して型変換と言うこともありそうです。

C#の場合あれば、(int)のような表記かasを使って行う処理をキャストというと定義してよさそうです。
それ以外は変換処理ですね。

VBの場合であれば、例1はDirectCast/TryCast(VB8.0以降。C#のasに当たるもの)を使ってキャストします。
例2は暗黙的な型変換(明示的に型変換をするには、CInt、CTypeを使います)です。
例3は明示的な型変換が必要です。C#の場合と同様に丸め方に応じた方法で変換が必要でしょう。
(例3についてはC#でのキャストに当たるものはありません。あえて対応を考えるなら
CInt、CTypeですが挙動的には違います)
例4はInt32.Parseなどを使って処理します。例5は独自に実装して処理します。
(お勧めではないですが、例4にも例5にもCInt、CTypeを使うこともできます)

明らかにキャストと呼べるのは例1のみで、例2、例3については微妙です。
例4、例5については基本的にC#の場合と同様ですが、CInt、CTypeを使って処理する場合は、
キャストと呼ぶ人もいるかもしれません。

私としては、VBでは例1のみがキャストで後は型変換だと呼びたいところです。
この主張はテッテさんの投稿の以下の主張と同じですね。

引用:

テッテさんの書き込み (2009-01-08 16:44) より:
CInt で行われる「それぞれの文字を見たまま数字として解釈して、文字列を10進数として解釈する。」という処理はキャストというよりは変換だと思います。日本語だとわかりにくいのですが、英語でいうなら Cast と Convert の違いなのかなとも思います。



--

長い前置きでしたが、ここからが本題です。

引用:

未記入さんの書き込み (2009-01-09 12:36) より:
Ctype,Cint,DirectCastは仕方ないので使ってもよいとしなければならないことが
もどかしいです。



この中でキャストと呼べるのはDirectCastだけだと私は思いますが、
それ以外は使わなければならないということはなくて、使うかどうかを吟味した上で、
あえて使うならそれはそれで構わないと思います。

ただ、CTyep/CIntはやはりあまりお勧めしません。CIntの動作の特殊性が理由です。
CInt関数はご存知のとおり、VB6から引き継がれている関数です。その仕様は明確ではなく複雑怪奇なため、
どうしてもという理由がなければ、使うのを避けるようにした方がよいでしょう。

有名どころでは、「CInt("(123)")」は-123を返す、という仕様があります。
(業務によっては()表記でマイナスを表すことがあるためと思います)
こういった特殊仕様がいろいろありますので、意図しない結果を招くことがあります。

引用:

例えば
文字列から数値に変換する場合はParseを使うが、
全角文字列が入る場合はCintを使用するというのは混乱すると思うのです。
とりあえずCintまたはCtypeでと指導すると思います。



文字列中の全角数字を半角数字に置き換えるメソッドを作っておいて、その処理を通してからParseするか、
そもそもの全角数字も含めてOKな変換メソッドを独自に作るのも手ですね。

引用:

DBのフィールド値(Number型)を変換するのに
Cint(DirectCast(Field1,Double))と指導するのも混乱するのではと思うのです。
NullチェックをしてからCintでと指導すると思います。

ただConvert.ToInt32(DataTable1.Rows(0).Item(0).ToString) と書かれても
強く否定はできません・・・。



この例の場合、丸めが意識されていませんね。元が小数ならMath.〜なメソッドで
丸めを意識して処理すべきだと思います。

引用:

VBではきっちり書きたくても書けないのですね・・・。



というわけで、きっちり書けないわけではないです。


[ メッセージ編集済み 編集者: よねKEN 編集日時 2009-01-09 13:47 ]
テッテ
ベテラン
会議室デビュー日: 2008/03/16
投稿数: 91
投稿日時: 2009-01-09 15:51
よねKENさん、わかりやすいまとめありがとうございます。

なるほど、私は C# をメインで使っているので、C# のキャスト演算子で処理できるものをキャストと理解しています。そういう意味でよねKENさんの例2や例3もキャストと明言していましたが、意味的に考えてみると、そんな単純に決められるものでもなさそうですね。

まあ個人差があってもいい部分だとも思いますし、そんなにこだわっている部分ではないのですが。

引用:


引用:


DBのフィールド値(Number型)を変換するのに
Cint(DirectCast(Field1,Double))と指導するのも混乱するのではと思うのです。
NullチェックをしてからCintでと指導すると思います。

ただConvert.ToInt32(DataTable1.Rows(0).Item(0).ToString) と書かれても
強く否定はできません・・・。




この例の場合、丸めが意識されていませんね。元が小数ならMath.〜なメソッドで
丸めを意識して処理すべきだと思います。




よねKENさんのこの発言を見て気づきましたが、速度の問題よりも、もっと致命的な問題がありましたね。浮動小数点型の場合、ToString してから Parse すると精度が落ちてしまうことがあります。

引用:


ただ、CTyep/CIntはやはりあまりお勧めしません。




私は CInt は使いませんが、CType は使わざるを得ないこともあると思っています。

よねKENさんの挙げられた例1〜5では使う必要はないと思いますが、以下だとどうでしょう?

例6) Decimal型の変数に入っている値が、情報を失うことなく Int32 に変換できるとわかっている場合に、それを○○○してInt32型の変数に入れる。

例7) 型Aから型Bへの縮小変換が定義されていていて、型Aの変数に入っている型Aの値を○○○して型Bの変数に入れる。(演算子がオーバーロードされているという意味です。)

私はこういう場合、CType を使わざるを得ないと思っています。



以下は質問者さんにですが、

引用:

DBのフィールド値(Number型)を変換するのに
Cint(DirectCast(Field1,Double))と指導するのも混乱するのではと思うのです。



混乱すると考える理由は何でしょう?むしろ、型をしっかり意識することを教えないとまずいと思います。

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