106

doubleonstring値をキャストする関数があります。

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()しないのですか?

4

10 に答える 10

142

どういうわけか同等と思われるかもしれませんが、目的はまったく異なります。まず、キャストとは何かを定義してみましょう。

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

これは少し一般的で、キャストは変換と同じ構文を持っていることが多いため、変換と何らかの形で同等です。そのため、言語でキャスト(暗黙的または明示的)が許可されている場合と、(詳細)明示的な変換?

まず、それらの間に簡単な線を引きます。正式には(言語構文と同等であっても)、キャストはタイプを変更しますが、変換は値を変更します/変更する可能性があります(最終的にはタイプと一緒に)。また、キャストはリバーシブルですが、変換はリバーシブルではない場合があります。

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

暗黙のキャスト

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;

この場合、コンパイラーはそれが実装されていること、およびそれが(から派生している)ことを知っているので、キャストは暗黙的です。オブジェクトはタイプを変更しないため(キャストを使用すると別のタイプの新しいオブジェクトを作成するため、これはs型とプリミティブ型では異なります)、情報が失われることはありません。変更されるのはオブジェクトのビューです。stringIFormattableNotSupportedExceptionExceptionstruct

明示的なキャスト

変換がコンパイラーによって暗黙的に行われない場合、キャストは明示的であり、キャスト演算子を使用する必要があります。通常、それは次のことを意味します。

  • 情報やデータが失われる可能性があるため、注意する必要があります。
  • 変換が失敗する可能性があるため(一方のタイプをもう一方のタイプに変換できないため)、繰り返しになりますが、何をしているのかを知っておく必要があります。

プリミティブ型

変換中に一部のデータが失われる可能性がある場合は、プリミティブ型に明示的なキャストが必要です。次に例を示します。

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ですか?キャストを使用したC#ではコンパイラに次のように言うため、実際にはConvert実装と実装に起因する微妙な違いを無視します。IConvertible

私を信じてください、このタイプはあなたが今それを知ることができなくてもそのタイプです、私にそれをさせてください、そしてあなたは見るでしょう。

-また-

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

それ以外の場合は、より明示的な操作が必要です(簡単なキャストの影響について考えてください。そのため、C ++では長くて冗長で明示的な構文が導入されています)。これには複雑な操作が含まれる場合があります(string->double変換の場合は解析が必要になります)。stringたとえば、への変換は(ToString()メソッドを介して)常に可能ですが、期待するものとは異なる意味を持つ可能性があるため、キャストよりも明示的である必要があります(書くほど、自分がしていることについて考えるようになります)。

この変換は、カスタム変換演算子(キャストするクラスで定義)またはより複雑なメカニズム(TypeConverterたとえば、sまたはクラスメソッド)を使用して、オブジェクト内で(そのための既知のIL命令を使用して)実行できます。何が起こるかはわかりませんが、失敗する可能性があることはわかっています(そのため、より制御された変換が可能な場合はIMOを使用する必要があります)。あなたの場合、変換は単にを解析して:stringを生成します。double

double value = Double.Parse(aStringVariable);

もちろん、これは失敗する可能性があるため、これを行う場合は、スローされる可能性のある例外を常にキャッチする必要があります(FormatException)。ここではトピックから外れていますが、aTryParseが使用可能な場合は、それを使用する必要があります(意味的には、数値ではない可能性があり、失敗するのがさらに速いためです)。

.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文字列からの変換。

結論

IMOConvertは、キャストで同じ変換を実行できる場合でも(他に何かが利用可能でない限り)、変換が失敗する可能性があることがわかっている場合は常に使用する必要があります(形式、範囲、またはサポートされていない可能性があるため)。誰があなたのコードを読むのか、あなたの意図は何か、そしてそれが失敗するかもしれないことを明らかにします(デバッグを簡素化します)。

他のすべてについては、キャストを使用する必要がありますが、選択の余地はありませんが、別のより良い方法が利用できる場合は、それを使用することをお勧めします。stringあなたの例では、からへの変換doubleは(特にテキストがユーザーからのものである場合)非常に頻繁に失敗するものであるため、たとえばTryParseメソッドを使用して、可能な限り明示的にする必要があります(さらに、より詳細に制御できます)。

編集:それらの違いは何ですか?

更新された質問と私が以前に書いたもの(キャストを使用できるときConvertと使用する必要があるときとの比較)を維持することによると、明確にする最後のポイントは、それらの間に違いがあるかどうかです(さらに、操作を実行できるようにConvert使用IConvertibleIFormattableインターフェイスキャストでは許可されていません)。

簡単な答えは「はい」です。動作が異なります。Convertこのクラスはヘルパーメソッドクラスのように見えるので、多くの場合、いくつかの利点やわずかに異なる動作を提供します。例えば:

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

かなり違いますよね?キャストは切り捨てられますが(これは私たち全員が期待することです)、Convert最も近い整数への丸めを実行します(これは、知らない場合は予期されない場合があります)。それぞれの変換方法には違いがあるため、一般的なルールを適用することはできず、ケースバイケースで確認する必要があります...他のすべてのタイプに変換するための19の基本タイプ...リストはかなり長くなる可能性があります。場合!

于 2013-03-13T20:29:08.327 に答える
13

キャストはコンパイラーに「この変数はバーだと思っていることは知っていますが、たまたまあなたよりも多くのことを知っています。オブジェクトは実際にはFooなので、FooからのFooであるかのように扱います。今。" 次に、実行時に、実際のオブジェクトが実際にFooであることが判明した場合、コードは機能します。オブジェクトがFooでなかったことが判明した場合、例外が発生します。(具体的にはSystem.InvalidCastException。)

一方、変換は、「Barタイプのオブジェクトを指定すると、そのBarオブジェクトの内容を表す新しいFooオブジェクトを作成できます。元のオブジェクトは変更しません。変更しません」という言い方です。元のオブジェクトを別の方法で処理すると、他の値に基づいた新しいものが作成されます。それをどのように行うかについては、何でもかまいませんConvert.ToDoubleDouble.Parseこれには、どのタイプの文字列がどの数値を表すかを決定するためのあらゆる種類の複雑なロジックがあります。文字列をdoubleに異なる方法でマッピングする独自の変換メソッドを作成できます(おそらく、ローマ数字などの数字を表示するためのまったく異なる規則をサポートするためです)。変換は何でもできますが、実際にはコンパイラーに何かをするように要求しているわけではありません。コンパイラは、あなたの助けがなければ、(例として)astringをにマップする方法を知る方法がないため、新しいオブジェクトを作成する方法を決定するコードを書いているのはあなたdoubleです。

では、いつ変換し、いつキャストしますか?どちらの場合も、あるタイプの変数、たとえばAがあり、タイプBの変数が必要です。Aオブジェクトが実際に内部でBである場合、キャストします。それが実際にはBでない場合は、それを変換し、プログラムがAからBを取得する方法を定義する必要があります。

于 2013-03-13T19:21:37.057 に答える
6

差出人MSDN

明示的な変換(キャスト):明示的な変換にはキャスト演算子が必要です。変換中に情報が失われる可能性がある場合、または他の理由で変換が成功しない可能性がある場合は、キャストが必要です。典型的な例としては、精度や範囲が狭い型への数値変換や、基本クラスインスタンスから派生クラスへの変換などがあります。

次の例を考えてみましょう。

double a = 2548.3;
int b;
b = (int)a; //2548 --> information (.3) lost in the conversion

そしてまた:

キャストは、変換を行う予定であり、データ損失が発生する可能性があることを認識していることをコンパイラーに明示的に通知する方法です。

互換性のないSystem.Convertタイプ間で変換する場合は、クラスを使用できます。キャスト変換主な違いは、コンパイル実行時です。型変換の例外は実行時に表示されます。つまり、実行時に失敗した型キャストにより、がスローされます。 InvalidCastException


結論:aキャストでは、実際に型であるコンパイラーに指示しているbので、そうであれば、プロジェクトは次の例のようなエラーなしでビルドされます。

double s = 2;
int a = (int) s;

aしかし、変換では、型から新しいオブジェクトを作成する方法があると言っています。bそれを実行して、エラーなしでプロジェクトをビルドしてください。ただし、実行時に型キャストが失敗するとInvalidCastException、投げられる

たとえば、以下のコードは、型の式DateTimeを型にキャストできないことをコンパイラが検出するため、コンパイルされませんint

DateTime s = DateTime.Now;
int a = (int)(s);

しかし、これは正常にコンパイルされています。

DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);

しかし、実行時に、次のようになりInvalidCastExceptionます。

'DateTime'から'Int32'へのキャストが無効です。

于 2016-03-05T07:16:47.453 に答える
4

あなたの例では、文字列をdouble(非整数型)にキャストしようとしています。

それが機能するには、明示的な変換が必要です。

また、intに変換すると、double値の小数部分が失われる可能性があるためConvert.ToDouble、代わりに使用できた可能性があることを指摘する必要があります。Convert.ToInt64

変数の値が「5.25」の場合、varDoubleは5.00になります(Int64への変換のために0.25の損失)

キャストと変換に関する質問に答える。

キャスト(明示的なキャスト)が明示的なキャストの要件を満たしていません。キャスト演算子を使用してキャストしようとしている値が無効です(つまり、整数ではありません)。

キャスト/変換のルールについては、このMSDNページにアクセスしてください

于 2013-03-13T19:01:51.000 に答える
4

このConvert.Doubleメソッドは、実際には内部的にDouble.Parse(string)メソッドを呼び出すだけです。

タイプStringもタイプもDouble2つのタイプ間の明示的/暗黙的な変換を定義しないため、キャストは常に失敗します。

このDouble.Parseメソッドは、の各文字を調べ、stringの文字の値に基づいて数値を作成しますstring。いずれかの文字が無効な場合、Parseメソッドは失敗します(Convert.Doubleメソッドも失敗します)。

于 2013-03-13T18:58:20.427 に答える
4

キャストには変換は含まれません。つまり、値の内部表現は変更されません。例:

object o = "Hello"; // o is typed as object and contains a string.
string s = (string)o; // This works only if o really contains a string or null.

あなたはこのように変換することができdoubleますstring

double d = 5;
string s = d.ToString(); // -> "5"

// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"

stringいくつかの方法でaをaに変換できdoubleます(ここではそのうちの2つだけ)。

string s = "5";
double d = Double.Parse(s); // Throws an exception if s does not contain a valid number

または安全な方法

string s = "5";
double d;
if (Double.TryParse(s, out d)) {
    Console.WriteLine("OK. Result = {0}", d);
} else {
    Console.WriteLine("oops!");
}
于 2013-03-13T19:14:55.290 に答える
2

double varDouble = (double)variablevariableそれはすでにdoubleであると想定しています。がdoubleでない場合variable(文字列)、これは失敗します。 double varDouble = Convert.ToDouble(variable)それが言うように-それは変換します。解析またはその他の方法でdoubleを抽出できる場合はvariable、それを実行します。

私は2番目に、Double.ParseまたはDouble.TryParse何が起こっているのかをより明確に示しているために使用します。あなたは文字列から始めて、それがdoubleに変換可能であることを期待しています。疑問がある場合は、を使用してTryParseください。

がメソッド引数の場合variable、タイプをdoubleに変更します。発信者に正しいタイプを提供する責任を負わせます。そうすれば、コンパイラが自動的に機能します。

于 2016-03-05T03:36:15.280 に答える
1
string variable = "5.00";     
double varDouble = (double)variable;

上記の変換は、言語では許可されていません。数値タイプの明示的なキャストのリストは次のとおりです。http://msdn.microsoft.com/en-us/library/yht2cx7b.aspxご覧のとおり、すべての数値タイプを別の数値タイプに変換できるわけではありません。

ここでキャストに関するいくつかの詳細情報

そして、これはConvert.ToDouble()とどのように異なりますか?

タイプをキャストしても、データ構造は変更されません。さて、数値変換の場合、それはいくつかのビットを失うか、いくつかの追加の0ビットを得るかもしれません。しかし、あなたはまだ数を扱っています。その数が占めるメモリの量を変更しているだけです。これは、コンパイラが必要なすべてを実行するのに十分安全です。

ただし、文字列を数値にキャストしようとすると、変数が使用するメモリの量を変更するだけでは不十分なため、それを行うことはできません。たとえば、5.00文字列は「数字」のシーケンスであるため:53(5)46(。)48(0)48(0)-これはASCII用ですが、文字列には同様のものが含まれます。コンパイラが文字列から最初のN(doubleの場合は4?わからない)バイトを取得する場合、その部分には完全に異なるdouble番号が含まれます。同時に、Convert.ToDouble()は、文字列の各記号を取得し、それが表す数字を計算し、文字列が数値を表す場合は二重の数値を作成する特別なアルゴリズムを実行します。PHPのような言語は、大まかに言えば、バックグラウンドでConvert.ToDoubleを呼び出します。しかし、静的に型付けされた言語のように、C#はあなたのためにそれをしません。これにより、すべての操作がタイプセーフであり、次のような予期しない動作が発生しないことを確認できます。

double d = (double)"zzzz"
于 2013-03-13T19:02:18.903 に答える
1

このような文字列をdoubleにキャストすることは、C#では許可されていません。そのため、例外が発生します。文字列を変換する必要があります(受け入れ可能な変換パスを示すMSDNドキュメント)。これは、文字列に必ずしも数値データが含まれるとは限らないためですが、さまざまな数値型には含まれます(null値を除く)。AConvertは、文字列をチェックして数値に変換できるかどうかを確認するメソッドを実行します。可能であれば、その値を返します。できない場合は、例外をスローします。

それを変換するには、いくつかのオプションがあります。あなたConvertはあなたの質問でメソッドを使用しました、Parseそれは大部分がに似ています、しかしあなたはあなたがすることを可能にするであろうTryParseConvertも見るべきです:

string variable = "5.00"; 

double varDouble;

if (Double.TryParse(variable, out varDouble)) {
    //Code that runs if the conversion succeeded.
} else {
    //Code that runs if the conversion failed.
}

これにより、数値以外の文字列を実行しようとしたConvert場合に発生する可能性のある例外を回避できます。Parse

于 2013-03-13T19:03:45.317 に答える
-1

最も重要な違いは、型キャストが使用され、変換が失敗した場合(たとえば、非常に大きなfloat値をintに変換している場合)、例外はスローされず、intが保持できる最小値が表示されることです。ただし、Convertを使用する場合、そのようなシナリオでは例外がスローされます。

于 2019-01-05T21:03:26.107 に答える