1. ホーム
  2. c#

[解決済み] キャストとConvert.To()メソッドの違いについて

2022-12-17 14:11:02

質問

をキャストする関数があります。 doublestring の値を指定します。

string variable = "5.00"; 

double varDouble = (double)variable;

コード変更がチェックインされ、プロジェクトがビルドされるとエラーが発生します。 System.InvalidCastException: Specified cast is not valid.

しかし、以下を実行すると...

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);

...プロジェクトは何のエラーもなくビルドされます。

をキャストするのと Convert.To() メソッドを使うことの違いは何ですか? なぜキャストすると Exception を投げるのか、そして Convert.To() を使うのとは違うのですか?

どのように解決するには?

たとえ としても、その目的は全く異なるものです。まず、キャストが何であるかを定義してみましょう。

キャストとは、あるデータ型のエンティティを別のデータ型に変更することです。

ちょっと汎用的なもので、なんとなく相当するのは 変換 と同じで、キャストはしばしば変換と同じ構文を持っているので、質問は次のようになるはずです。 いつ(暗黙的または明示的な)キャストが言語によって許され、いつ(より)明示的な変換を使わなければならないか?

まず を描く の間に簡単な線を引いてみましょう。形式的には(言語の構文としては同等でも)キャストは型を変更し、変換は値を変更する(かもしれない)のですが、最終的には 一緒に を変更します)。また、キャストは可逆的ですが、変換はそうではないかもしれません。

このトピックはかなり広大なので、カスタムのキャスト演算子をゲームから除外することで少し絞り込んでみましょう。

暗黙のキャスト

C#ではキャストは 暗黙のキャストです。 (となります(このチェックは 型に対して行われ、実際の値に対しては行われないことに注意してください ).

原始的な型

例えば

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

これらのキャストは暗黙的なものです。なぜなら、変換中に情報を失うことがないからです(単に型が広くなるだけです)。逆に暗黙のキャストは、実際の値に関係なく(実行時にしか確認できないため)、変換時に何らかの情報を失う可能性があるため、許可されません。例えば、このコードはコンパイルできません。 double が含まれる可能性があり(実際に含まれます)、その値は float :

// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;

対象物

オブジェクト(へのポインタ)の場合、コンパイラがソースの型がターゲットクラスの派生クラスである(またはそれを実装している)ことを確認できる場合など、キャストは常に暗黙のうちに行われます。

string text = "123";
IFormattable formattable = text;

NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;

この場合、コンパイラが が知っている その string を実装しています。 IFormattable で、その NotSupportedException は(から派生して) Exception であり、キャストは暗黙のうちに行われます。オブジェクトはその型を変更しないので、情報は失われません(この点は struct やプリミティブ型とは異なりますが、キャストによって の新しいオブジェクトを作成するからです。 )、何が変わるかというと、あなたの ビュー はどのようなものですか?

明示的なキャスト

キャストは、コンパイラが暗黙のうちに変換を行わず、キャスト演算子を使用しなければならない場合に明示的に行われます。通常はそれを意味します。

  • 情報やデータを失う可能性があるので、注意が必要です。
  • 変換に失敗する可能性があります (一方の型から他方の型に変換できないため) ので、やはり注意が必要です。

プリミティブ型

プリミティブ型は、変換時にデータを失う可能性がある場合など、明示的なキャストが必要です。

double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;

float epsilon = (float)Double.Epsilon;

どちらの例でも、値が float の範囲にあるとしても,情報(この場合は精度)を失うことになるので,変換は明示的に行わなければなりません。では、これを試してみましょう。

float max = (float)Double.MaxValue;

この変換は失敗します。ですから、繰り返しになりますが、それを認識し、チェックを行うことができるように明示しなければなりません(この例では、値は一定ですが、実行時の計算やI/Oから来るかもしれません)。例に戻ります。

// won't compile!
string text = "123";
double value = (double)text;

これはコンパイラがテキストを数値に変換できないため、コンパイルできません。テキストは数字だけでなくあらゆる文字を含むことができ、これは C# では明示的なキャストであっても多すぎます(ただし、他の言語では許可されている場合があります)。

オブジェクト

ポインタからオブジェクトへの変換は、型が無関係な場合、失敗することがあります。例えば、このコードはコンパイルできません(コンパイラは変換が不可能であることを知っているからです)。

// won't compile!    
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";

このコードはコンパイルできますが、ランタイムに InvalidCastException :

object obj = GetNextObjectFromInput();
string text = (string)obj;

obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;

換算表

では最後に、もしキャストが変換であるならば、なぜ私たちは以下のようなクラスを必要とするのでしょうか? Convert ? という微妙な違いを無視して Convert の実装と IConvertible の実装は、実際には、C#ではキャストでコンパイラに言うからです。

信じてください、この型はあの型です。たとえ今は分からなくても、やってみれば分かります。

-あるいは

<ブロッククオート

心配しないでください、この変換で何かが失われることは気にしません。

それ以外のものについては より の意味を考えてみてください。 簡単なキャスト の意味を考えてみてください。そのためにC++は長く冗長で明示的な構文を導入したのです)。これは複雑な操作を伴うかもしれません(例えば string -> double の変換はパースが必要になります)。への変換は string への変換は常に可能です(例えば ToString() メソッドで) 可能ですが、期待するものとは異なる意味になる可能性があるので、キャスト ( 書けば書くほど、自分が何をしているのかが分かってきます。 ).

この変換はオブジェクトの内部で行われ(そのための既知のIL命令を使用)、カスタムの変換演算子(キャストするクラスで定義)を使用するか、より複雑なメカニズム( TypeConverter s やクラスメソッドなど)。それを行うために何が起こるかわからないが、失敗する可能性があることは承知している(だからこそIMOでは、より多くの を制御する 変換が可能な場合、それを使用すべきです。) あなたの場合、変換は単に string を生成するために double :

double value = Double.Parse(aStringVariable);

もちろん、これは失敗する可能性があるので、もしこれをやるなら、投げられるかもしれない例外を常にキャッチする必要があります ( FormatException ). ここでの話題からは外れますが、もし TryParse が利用可能な場合は、それを使うべきです(意味的には と言う は数字でない可能性があり、さらに速く...失敗するからです)。

.NETにおける変換は、多くの場所から来ることができます。 TypeConverter の実装、ユーザー定義の変換演算子による暗黙的/明示的なキャスト。 IConvertible の実装、パージングメソッド(何か忘れてるかな?) これらの詳細については、MSDN を参照してください。

この長い答えを終えるために、ユーザー定義の変換演算子について少し話をします。それは単に 砂糖 プログラマがある型を別の型に変換するためにキャストを使用できるようにするためのです。これはクラス(キャストされるクラス)の中のメソッドで、「もし彼/彼女がこの型をあの型に変換したいのなら、私はそれを行うことができます」と言うものです。例えば

float? maybe = 10; // Equals to Nullable<float> maybe = 10;
float sure1 = (float)maybe; // With cast
float sure2 = maybe.Value; // Without cast

この場合、失敗する可能性があるので明示的になっていますが、これは(たとえこれに関するガイドラインがあったとしても)実装に任されています。このようなカスタム文字列クラスを書いたとします。

EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double

実装では、プログラマの生活を楽にするために、この変換をキャストで行うことにするかもしれません(より少なく書くためのショートカットであることを忘れないでください)。言語によっては、これを許可する場合もあります。

double value = "123";

任意の型への暗黙の変換を許可する(チェックは実行時に行われる)。適切なオプションを使用すれば、これは例えばVB.NETで行うことができます。ただ、哲学が違うだけです。

それらを使って何ができるのでしょうか?

では、最後の問題は、いつどちらかを使うべきかということです。では、どのような場合に明示的なキャストを使用できるのか見てみましょう。

  • 基本型間の変換。
  • からの変換 object から他の型への変換(これにはアンボックスも含まれる場合があります)。
  • 派生クラスから基底クラスへの変換 (または実装されたインターフェースへの変換)。
  • カスタムの変換演算子による、ある型から別の型への変換。

最初の変換のみ Convert を使うことができるので、他のものは選択肢がなく、明示的なキャストを使う必要があります。

では、どのような場合に Convert :

  • 任意の基本型から別の基本型への変換 (いくつかの制限があります。 MSDN ).
  • を実装している任意の型からの変換は IConvertible を実装する任意の型から、他の(サポートされる)型への変換。
  • への変換は byte 配列から文字列へ/から文字列へ。

結論

IMO Convert は、たとえ同じ変換がキャストで行えるとしても(他の何かが利用可能でない限り)、変換が失敗するかもしれないと分かっているときはいつでも使われるべきです(フォーマットのため、範囲のため、またはサポートされていないかもしれないため)。 それはあなたのコードを読む人に、あなたの意図が何であるかを明確にします。 と失敗する可能性があることを明確にします(デバッグの簡略化)。

他のすべてについては、キャストを使用する必要があり、選択の余地はありません。あなたの例では string から double は、(特にユーザーからのテキストの場合)失敗することが多いので、できるだけ明示的にする必要があります(さらに、それをよりコントロールできるようになります)。 TryParse メソッドを使うなどです。

編集部:両者の違いは何ですか?

更新された質問によると、私が前に書いたこと(約 いつ を使うことができる/しなければならないときと比較して、キャストを使うことができます。 Convert を使うことができる/しなければならないときと比較して)、最後にはっきりさせるべき点は、両者に違いがあるかどうかです(さらに言えば ConvertIConvertibleIFormattable インターフェースを持つため、キャストでは許されない操作を行うことができます)。

短い回答は はい、それらは異なる動作をします。 . 私は Convert クラスはヘルパーメソッドクラスのようなもので、しばしば、いくつかの 利点 またはわずかに異なる動作を提供します。例えば

double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2

かなり違いますよね?キャストは切り捨てますが(皆が予想していることです)。 Convert は最も近い整数への丸めを行います (そして、これは意識していないと予想できないかもしれません)。19 の基本型から他のすべての型への変換...リストはかなり長くなるので、ケースバイケースで MSDN を参照する方がよいでしょう。