どういうわけか同等と思われるかもしれませんが、目的はまったく異なります。まず、キャストとは何かを定義してみましょう。
キャストとは、あるデータ型のエンティティを別のデータ型に変更するアクションです。
これは少し一般的で、キャストは変換と同じ構文を持っていることが多いため、変換と何らかの形で同等です。そのため、言語でキャスト(暗黙的または明示的)が許可されている場合と、(詳細)明示的な変換?
まず、それらの間に簡単な線を引きます。正式には(言語構文と同等であっても)、キャストはタイプを変更しますが、変換は値を変更します/変更する可能性があります(最終的にはタイプと一緒に)。また、キャストはリバーシブルですが、変換はリバーシブルではない場合があります。
このトピックはかなり広大なので、ゲームからカスタムキャスト演算子を除外して、少し絞り込んでみましょう。
暗黙のキャスト
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型とプリミティブ型では異なります)、情報が失われることはありません。変更されるのはオブジェクトのビューです。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
ですか?キャストを使用した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
使用IConvertible
とIFormattable
インターフェイスキャストでは許可されていません)。
簡単な答えは「はい」です。動作が異なります。Convert
このクラスはヘルパーメソッドクラスのように見えるので、多くの場合、いくつかの利点やわずかに異なる動作を提供します。例えば:
double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2
かなり違いますよね?キャストは切り捨てられますが(これは私たち全員が期待することです)、Convert
最も近い整数への丸めを実行します(これは、知らない場合は予期されない場合があります)。それぞれの変換方法には違いがあるため、一般的なルールを適用することはできず、ケースバイケースで確認する必要があります...他のすべてのタイプに変換するための19の基本タイプ...リストはかなり長くなる可能性があります。場合!