キャストと型変換は、式をあるデータ型から別のデータ型に変える、異なる2つの方法です。
C#のドキュメントでは、型変換はas演算子またはis演算子の使用として分類されています。キャストはキャスト演算子の使用と定義されています。
C#では、数値やクラス階層の話を離れると、文字列を整数に変換するInt32.Parse()のような変換先の型のメンバーを呼び出したり、変換元の型のメンバー(例えばobject.ToString())を呼び出したりして変換しなければならないことがよくあります。JavaScriptや他の動的言語の開発者は、この点に注意してください。
なお、暗黙的および明示的なキャスト演算子(operator-overloadingで説明しています)を定義すると、自分の型にかなり自由なキャストを持ち込めることにも触れておきます。
C#の型システムは、_C_やJavaScriptよりもいくぶん厳格で、その結果としてキャスト操作にもより多くの制限があります。暗黙的なキャストは、変換先の型が変換元の値の桁と符号を保持できる限り、2つの数値型の間で行われます。なお、ドキュメントには、実数への変換で精度が失われる可能性があるという例外が記されています。
暗黙的なキャストに、特別な構文はありません。例えば次のとおりです。
int myInt = 1729;
long myLong = myInt;
数値(または文字列)の式をboolに暗黙的に変換することはできません。このための変換には、基底クラスライブラリ(BCL)が提供するConvert.ToBoolean()を使います。
char型の式は、intに暗黙的にキャストできます。逆方向のキャストは明示的に行う必要があります。intのすべての値が有効なUTF-16文字というわけではありません。
数値型を暗黙的にキャストできない場合でも、たいていは明示的なキャスト演算子を使えます。
キャストする値が、変換先の型の幅が足りない、あるいは符号が合わないといった理由で変換先の型で表現できない場合、整数ではオーバーフロー例外がスローされることがあり、floatやdoubleでは変換先の変数がInfinityという値になることがあります。
int型の式は、charに明示的にキャストできます。その結果、無効なcharになることがあります。
int largeInt = Int32.MaxValue;
int largeNegInt = Int32.MinValue;
ushort shortUnsignedInt = ushort.MaxValue;
float largeFloat = float.MaxValue;
float smallFloat = 17.29f;
// implicit cast
int from_ushort = shortUnsignedInt; // 65535
float from_int = largeInt; // -21474836E+09
int from_char = 'a'; // 96
// explicit cast
uint from_largeInt = (uint)largeInt; // 2147483647
uint from_neg = (uint) largeNegInt; // 2147483648 or OverflowException is thrown (if checked)
int from_smallFloat = (int) smallFloat; // 17
int from_largeFloat = (int) largeFloat; // -2147483648 or OverflowException is thrown (if checked)
char from_intc = (char) 32; // ' '
char from_invalid_int = (char) 0xdcbf; // invalid char - no exception thrown
// no cast available
int fromString = Int32.Parse("42"); // 42
string toString = largeInt.ToString(); // "2147483647"
int fromString_bad = Int32.Parse("forty two"); // FormatException is thrown
_checked_キーワードについては、こちらの記事を参照してください。
どの型も、その基底クラスやインターフェースに暗黙的に変換できます。
IList<int> ll = new List<int>();
基底クラスから派生クラスへの変換には、キャスト演算子を使えます。
IList<int> ll = new List<int>();
List<int> l = (List<int>)ll;
キャストする式の型が、キャスト先の型と関係がないためにキャストが失敗すると、InvalidCastExceptionのインスタンスがスローされます。
こうした型変換では、isキーワードを使うのがより一般的な方法です。
interface IFoo { int Bar(); }
class Foo : IFoo { public int Bar() { return 42; }}
Object r = new Random();
IFoo ifoo = new Foo();
Object o = new Foo();
int result = 1729;
if (ifoo is Foo foo)
{
result = foo.Bar();
}
// result == 42
result = 1729;
if (o is Foo foo2)
{
result = foo2.Bar();
}
// result == 42
result = 1729;
if (r is Foo foo3)
{
result = foo3.Bar();
}
// result == 1729
asキーワードは、isと似た役割を果たします。例えばvar foo = ifoo as Foo;のように使います。この例では、ifooがFoo型でなければfooはnullになり、そうでなければifooがそのまま代入されます。
型は、独自の明示的・暗黙的なキャスト演算子を定義できます。
BCLにおける明示的キャストと暗黙的キャストの例としては、BigInteger構造体と他の数値型との相互変換があります。
typeofを使うオブジェクトの正確な型を調べたい場合、isは少し寛容すぎるかもしれません。isはオブジェクトを、あるクラスやその基底クラスのいずれかに変換してしまうからです。こういうときは、typeofとObject.GetType()が解決策になります。
object o = new List<int>();
o is ICollection<int> // true
o.GetType() == typeof(ICollection<int>) // false
o is List<int> // true
o.GetType() == typeof(List<int>) // true