C#のキャスト完全ガイド|型変換・as/is・Convertの違いとエラー対策を初心者向けに解説

はじめに

C#でプログラムを書いていると、ある値を別の型として扱いたい場面がよくあります。たとえば、doubleintに変換したい、object型に入っている値をstringとして取り出したい、基底クラスの変数を派生クラスとして扱いたい、といったケースです。

このようなときに使うのが「キャスト」や「型変換」です。

ただし、C#のキャストにはいくつかの方法があります。

C#
int number = (int)3.14;
string text = obj as string;

if (obj is string value)
{
Console.WriteLine(value);
}

int result = Convert.ToInt32("123");

一見どれも「型を変える処理」に見えますが、使いどころや失敗したときの挙動は異なります。誤った使い方をすると、InvalidCastExceptionFormatExceptionOverflowExceptionNullReferenceExceptionなどのエラーにつながります。

この記事では、C#のキャストについて、キャスト演算子、asisConvertParseTryParseの違いを初心者向けに整理しながら解説します。

1. C#のキャストとは?型変換との違いを初心者向けに整理

C#のキャストとは、ある値を別の型として扱うための操作です。

たとえば、次のようにdouble型の値をint型として扱う場合にキャストを使います。

C#
double price = 123.45;
int intPrice = (int)price;

Console.WriteLine(intPrice); // 123

この例では、double型の123.45int型に変換しています。小数部分は切り捨てられ、結果は123になります。

C#では型が厳密に管理されているため、異なる型の値をそのまま代入できないことがあります。そのような場合に、キャストや型変換を使って型を合わせます。

1-1. キャストは「別の型として扱う」ための明示的な型変換

キャストは、基本的に「この値をこの型として扱う」とプログラマーが明示する書き方です。

代表的な構文は次のとおりです。

C#
変換先の型 変数名 = (変換先の型)変換元の値;

具体例は次のようになります。

C#
double value = 10.8;
int number = (int)value;

(int)の部分がキャストです。

このように、キャストは「変換先の型」を明示して行います。C#では、変換によって情報が失われる可能性がある場合や、安全性をコンパイラが保証できない場合には、明示的なキャストが必要になります。

1-2. 暗黙的な型変換と明示的な型変換の違い

C#の型変換には、大きく分けて「暗黙的な型変換」と「明示的な型変換」があります。

暗黙的な型変換は、キャストを書かなくても自動で行われる変換です。

C#
int number = 100;
double value = number;

Console.WriteLine(value); // 100

intからdoubleへの変換は、基本的に値が失われにくいため、C#が自動で変換してくれます。

一方、明示的な型変換は、キャストを書かなければならない変換です。

C#
double value = 123.45;
int number = (int)value;

Console.WriteLine(number); // 123

doubleからintへ変換すると、小数部分が失われます。そのため、C#は自動では変換せず、プログラマーに(int)という明示的なキャストを書かせます。

つまり、暗黙的な型変換は「安全な変換」、明示的な型変換は「情報が失われる可能性がある、または失敗する可能性がある変換」と考えると理解しやすいです。

1-3. キャストが必要になる代表的な場面

C#でキャストが必要になる代表的な場面には、次のようなものがあります。

数値型を変換する場合です。

C#
double height = 170.5;
int roundedDown = (int)height;

object型に入っている値を元の型に戻す場合です。

C#
object obj = "Hello";
string text = (string)obj;

基底クラスの変数を派生クラスとして扱う場合です。

C#
Animal animal = new Dog();
Dog dog = (Dog)animal;

また、APIやライブラリから返ってきた値がobject型や基底クラス型になっている場合にも、適切な型に変換して使うことがあります。

1-4. キャストでできること・できないこと

キャストを使うと、数値型同士の変換や、継承関係にあるクラス同士の変換などができます。

C#
int number = 10;
double value = number;

double pi = 3.14;
int intPi = (int)pi;

また、object型に格納された値を、実際の型に戻すこともできます。

C#
object obj = "C#";
string text = (string)obj;

しかし、キャストは万能ではありません。

たとえば、文字列"123"intにするために、次のようなキャストはできません。

C#
string text = "123";

// エラー
// int number = (int)text;

stringからintへ変換したい場合は、キャストではなくint.Parseint.TryParseConvert.ToInt32などを使います。

C#
string text = "123";
int number = int.Parse(text);

キャストは「変換できる関係にある型」を扱う仕組みであり、文字列の内容を解釈して数値に変換するような処理とは異なります。

2. C#で使う主なキャスト・型変換の種類

C#で型を変換する方法は複数あります。代表的なものは次の6つです。

C#
(int)value
obj as string
obj is string
Convert.ToInt32(value)
int.Parse(text)
value.ToString()

それぞれ目的が異なるため、状況に応じて使い分けることが重要です。

2-1. キャスト演算子「(型)」を使う方法

キャスト演算子は、最も基本的なキャスト方法です。

C#
double value = 12.8;
int number = (int)value;

参照型でも使えます。

C#
object obj = "Hello";
string text = (string)obj;

ただし、変換できない型にキャストすると例外が発生します。

C#
object obj = 123;

// InvalidCastException
string text = (string)obj;

キャスト演算子は、成功することが分かっている場合や、失敗したら例外として扱いたい場合に使います。

2-2. as演算子を使う方法

as演算子は、参照型などを安全に変換したいときに使います。

C#
object obj = "Hello";

string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

asは変換に失敗しても例外を投げず、nullを返します。

C#
object obj = 123;

string text = obj as string;

Console.WriteLine(text == null); // True

そのため、asを使った後は必ずnullチェックを行うのが基本です。

2-3. is演算子で型を判定する方法

is演算子は、変数が特定の型かどうかを判定するために使います。

C#
object obj = "Hello";

if (obj is string)
{
Console.WriteLine("string型です");
}

さらに、パターンマッチングを使うと、型判定と変数への代入を同時に行えます。

C#
object obj = "Hello";

if (obj is string text)
{
Console.WriteLine(text);
}

現在のC#では、この書き方が非常によく使われます。asで変換してからnullチェックするよりも、簡潔で安全に書けることが多いです。

2-4. Convertクラスを使う方法

Convertクラスは、数値や文字列などを別の基本型に変換するために使います。

C#
string text = "123";
int number = Convert.ToInt32(text);

Console.WriteLine(number); // 123

数値型同士の変換にも使えます。

C#
double value = 123.45;
int number = Convert.ToInt32(value);

Console.WriteLine(number);

ただし、Convert.ToInt32(double)は単純な切り捨てではなく、丸め処理を行います。小数部分を切り捨てたい場合は、キャスト演算子(int)を使うか、Math.FloorMath.Truncateなどを検討します。

2-5. Parse・TryParseを使う方法

ParseTryParseは、文字列を数値や日付などに変換するときによく使います。

C#
string text = "123";
int number = int.Parse(text);

ただし、変換できない文字列をParseすると例外が発生します。

C#
string text = "abc";

// FormatException
int number = int.Parse(text);

入力値が不確実な場合は、TryParseを使うのが安全です。

C#
string text = "123";

if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値に変換できません");
}

ユーザー入力やCSV、JSON、外部ファイルなど、形式が保証されない文字列を扱う場合は、TryParseがよく使われます。

2-6. ToStringで文字列に変換する方法

値を文字列に変換したい場合は、ToStringを使います。

C#
int number = 123;
string text = number.ToString();

Console.WriteLine(text); // "123"

多くの型はToStringメソッドを持っています。

C#
double value = 3.14;
DateTime now = DateTime.Now;

string valueText = value.ToString();
string dateText = now.ToString();

ただし、変数がnullの場合にToStringを呼び出すとNullReferenceExceptionが発生します。

C#
object obj = null;

// NullReferenceException
// string text = obj.ToString();

nullの可能性がある場合は、Convert.ToStringやnull条件演算子を使うと安全です。

C#
object obj = null;

string text1 = Convert.ToString(obj); // ""
string text2 = obj?.ToString(); // null

3. キャスト演算子「(型)」の使い方

キャスト演算子(型)は、C#のキャストで最も基本となる書き方です。

数値型の変換、objectから元の型への変換、クラスのアップキャスト・ダウンキャストなど、さまざまな場面で使われます。

3-1. 基本構文とシンプルな使用例

キャスト演算子の基本構文は次のとおりです。

C#
変換先の型 変数名 = (変換先の型)変換元の値;

例を見てみましょう。

C#
double value = 10.5;
int number = (int)value;

Console.WriteLine(number); // 10

(int)valueと書くことで、valueint型として変換しています。

参照型でも同じように使えます。

C#
object obj = "Hello";
string text = (string)obj;

Console.WriteLine(text); // Hello

この場合、objの中身は実際にはstringなので、stringへのキャストに成功します。

3-2. int・double・floatなど数値型のキャスト

数値型同士の変換では、キャストがよく使われます。

C#
double d = 123.45;
int i = (int)d;

Console.WriteLine(i); // 123

floatからintへの変換も同じです。

C#
float f = 99.9f;
int i = (int)f;

Console.WriteLine(i); // 99

intからdoubleのように、より広い範囲を扱える型への変換は暗黙的に行えます。

C#
int i = 100;
double d = i;

Console.WriteLine(d); // 100

一方、doubleからintのように情報が失われる可能性がある変換では、明示的なキャストが必要です。

C#
double d = 100.5;
int i = (int)d;

3-3. 小数から整数にキャストするときの注意点

doublefloatintにキャストすると、小数部分は切り捨てられます。

C#
double a = 3.9;
double b = -3.9;

int x = (int)a;
int y = (int)b;

Console.WriteLine(x); // 3
Console.WriteLine(y); // -3

ここで注意したいのは、四捨五入ではないという点です。

C#
double value = 3.9;

int castValue = (int)value;
int roundedValue = (int)Math.Round(value);

Console.WriteLine(castValue); // 3
Console.WriteLine(roundedValue); // 4

小数を整数にするときは、目的に応じて使い分けましょう。

切り捨てに近い処理ならキャストやMath.Truncate、小さい方向への丸めならMath.Floor、大きい方向への丸めならMath.Ceiling、四捨五入に近い処理ならMath.Roundを使います。

C#
double value = 3.9;

Console.WriteLine((int)value); // 3
Console.WriteLine(Math.Truncate(value)); // 3
Console.WriteLine(Math.Floor(value)); // 3
Console.WriteLine(Math.Ceiling(value)); // 4
Console.WriteLine(Math.Round(value)); // 4

3-4. object型から元の型へ戻すキャスト

object型は、C#の多くの型の基底となる型です。そのため、さまざまな値をobject型の変数に入れることができます。

C#
object obj = "Hello";

この値をstringとして使いたい場合は、キャストします。

C#
string text = (string)obj;

Console.WriteLine(text.Length);

ただし、objectに入っている実際の値と異なる型にキャストすると失敗します。

C#
object obj = 123;

// InvalidCastException
string text = (string)obj;

objの中身はintなので、stringにキャストすることはできません。

このような場合は、isで型を確認してからキャストすると安全です。

C#
object obj = 123;

if (obj is int number)
{
Console.WriteLine(number);
}

3-5. クラスのアップキャストとダウンキャスト

クラスの継承関係でもキャストが使われます。

次のようなクラスがあるとします。

C#
class Animal
{
public void Eat()
{
Console.WriteLine("食べます");
}
}

class Dog : Animal
{
public void Bark()
{
Console.WriteLine("吠えます");
}
}

派生クラスから基底クラスへの変換をアップキャストといいます。

C#
Dog dog = new Dog();
Animal animal = dog;

アップキャストは安全なので、明示的なキャストは不要です。

一方、基底クラスから派生クラスへの変換をダウンキャストといいます。

C#
Animal animal = new Dog();
Dog dog = (Dog)animal;

dog.Bark();

この場合、animalの実体はDogなので、Dogへのキャストに成功します。

しかし、実体がDogでない場合は失敗します。

C#
Animal animal = new Animal();

// InvalidCastException
Dog dog = (Dog)animal;

ダウンキャストは失敗する可能性があるため、isasを使って安全に処理するのが一般的です。

C#
if (animal is Dog dog)
{
dog.Bark();
}

3-6. キャストに失敗すると起きるInvalidCastException

キャスト演算子で変換できない型に変換しようとすると、InvalidCastExceptionが発生します。

C#
object obj = 100;

// InvalidCastException
string text = (string)obj;

このエラーは、「その型として扱うことはできない」という意味です。

よくある原因は次のようなものです。

C#
object obj = 123;
string text = (string)obj;

intの値をstringにキャストしようとしていますが、これはできません。文字列にしたい場合は、ToStringConvert.ToStringを使います。

C#
object obj = 123;
string text = obj.ToString();

Console.WriteLine(text); // "123"

また、文字列から数値にしたい場合もキャストではなく、ParseTryParseを使います。

C#
string text = "123";
int number = int.Parse(text);

4. as演算子の使い方とキャスト演算子との違い

as演算子は、C#で参照型の変換を安全に行いたいときによく使われます。

キャスト演算子との大きな違いは、変換に失敗したときの挙動です。キャスト演算子は例外を発生させますが、asnullを返します。

4-1. as演算子の基本構文

as演算子の基本構文は次のとおりです。

C#
変換先の型 変数名 = 変換元の値 as 変換先の型;

例を見てみましょう。

C#
object obj = "Hello";

string text = obj as string;

Console.WriteLine(text); // Hello

objの中身はstringなので、as stringによる変換に成功します。

4-2. asは失敗時に例外ではなくnullを返す

asの特徴は、変換に失敗しても例外が発生しないことです。

C#
object obj = 123;

string text = obj as string;

Console.WriteLine(text == null); // True

この例では、objの実体はintです。そのため、stringとして扱うことはできません。

キャスト演算子ならInvalidCastExceptionが発生します。

C#
object obj = 123;

// InvalidCastException
string text = (string)obj;

一方、asは失敗時にnullを返すため、プログラムを例外で止めずに処理を分岐できます。

C#
object obj = 123;

string text = obj as string;

if (text == null)
{
Console.WriteLine("string型ではありません");
}

4-3. asが使える型・使えない型

as演算子は、主に参照型に対して使います。

C#
object obj = "Hello";
string text = obj as string;

クラス型やインターフェイス型の変換にも使えます。

C#
interface IRunnable
{
void Run();
}

class Dog : IRunnable
{
public void Run()
{
Console.WriteLine("走ります");
}
}

object obj = new Dog();
IRunnable runnable = obj as IRunnable;

if (runnable != null)
{
runnable.Run();
}

一方、通常の値型にはそのまま使えません。

C#
object obj = 123;

// コンパイルエラー
// int number = obj as int;

intは値型であり、asの失敗時に返すnullを代入できないためです。

ただし、nullable型であれば使える場合があります。

C#
object obj = 123;

int? number = obj as int?;

if (number.HasValue)
{
Console.WriteLine(number.Value);
}

とはいえ、値型の判定と取り出しには、通常はisパターンを使うほうが分かりやすいです。

C#
object obj = 123;

if (obj is int number)
{
Console.WriteLine(number);
}

4-4. asを使うべきケース

asを使うべきなのは、変換に失敗する可能性があり、失敗時に例外ではなくnullとして扱いたい場合です。

たとえば、object型の値がstringかどうか分からない場合です。

C#
void PrintLength(object obj)
{
string text = obj as string;

if (text != null)
{
Console.WriteLine(text.Length);
}
}

また、インターフェイスを実装しているかどうかを確認したい場合にも使えます。

C#
object obj = new Dog();

IRunnable runnable = obj as IRunnable;

if (runnable != null)
{
runnable.Run();
}

ただし、現在のC#では、型判定と変数代入を同時に行うisパターンのほうが好まれる場面も多いです。

C#
if (obj is IRunnable runnable)
{
runnable.Run();
}

4-5. as使用後にnullチェックが必要な理由

asは失敗時にnullを返すため、変換後の変数をそのまま使うとNullReferenceExceptionが発生する可能性があります。

C#
object obj = 123;

string text = obj as string;

// NullReferenceException
Console.WriteLine(text.Length);

objstringではないため、textnullになります。その状態でtext.Lengthにアクセスするとエラーになります。

正しくは、次のようにnullチェックを行います。

C#
object obj = 123;

string text = obj as string;

if (text != null)
{
Console.WriteLine(text.Length);
}
else
{
Console.WriteLine("string型ではありません");
}

または、null条件演算子を使うこともできます。

C#
Console.WriteLine(text?.Length);

ただし、text?.Lengthtextnullの場合にnullを返すため、その後の処理でnullをどう扱うかを考える必要があります。

5. is演算子の使い方と安全な型判定

is演算子は、ある値が指定した型かどうかを判定するために使います。

C#のキャストでは、変換前に型を確認することが重要です。isを使うことで、無理なキャストによるInvalidCastExceptionを避けやすくなります。

5-1. is演算子の基本構文

is演算子の基本構文は次のとおりです。

C#
変数 is 

例を見てみましょう。

C#
object obj = "Hello";

if (obj is string)
{
Console.WriteLine("objはstring型です");
}

objの実体がstringであれば、条件式はtrueになります。

C#
object obj = 123;

if (obj is string)
{
Console.WriteLine("string型です");
}
else
{
Console.WriteLine("string型ではありません");
}

この例では、objの実体はintなので、string型ではありませんと表示されます。

5-2. キャスト前に型を確認する方法

キャスト演算子を使う前にisで型を確認すると、安全にキャストできます。

C#
object obj = "Hello";

if (obj is string)
{
string text = (string)obj;
Console.WriteLine(text.Length);
}

この書き方なら、objstringの場合だけキャストするため、InvalidCastExceptionを防げます。

ただし、現在は次のようにパターンマッチングを使うほうが簡潔です。

C#
object obj = "Hello";

if (obj is string text)
{
Console.WriteLine(text.Length);
}

この書き方では、objstringなら、textというstring型の変数に代入されます。

5-3. パターンマッチングで判定と代入を同時に行う

C#では、is演算子とパターンマッチングを組み合わせることで、型判定と代入を同時に行えます。

C#
object obj = "C#";

if (obj is string text)
{
Console.WriteLine(text.ToUpper());
}

objstringなら、textとして使えます。

値型にも使えます。

C#
object obj = 123;

if (obj is int number)
{
Console.WriteLine(number + 10);
}

asでは通常の値型を直接扱いにくいですが、isパターンなら自然に書けます。

また、条件を追加することもできます。

C#
object obj = 25;

if (obj is int age && age >= 20)
{
Console.WriteLine("成人です");
}

このように、isパターンは安全で読みやすい型判定に向いています。

5-4. isとasの違い

isasはどちらも型に関する処理で使いますが、目的が異なります。

isは、型を判定するために使います。

C#
if (obj is string)
{
Console.WriteLine("stringです");
}

asは、変換を試みて、失敗したらnullを返します。

C#
string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

現在は、次のようにisパターンを使うと、型判定と変換を一度に行えます。

C#
if (obj is string text)
{
Console.WriteLine(text);
}

そのため、単に「型を確認して使いたい」場合は、isパターンが分かりやすいです。

一方で、変換結果を後続処理で使い回したい場合や、nullを自然に扱いたい場合にはasも有効です。

5-5. switch式・switch文で型ごとに処理を分ける方法

複数の型ごとに処理を分けたい場合は、switch文やswitch式を使うと便利です。

C#
object value = "Hello";

switch (value)
{
case string text:
Console.WriteLine($"文字列: {text}");
break;

case int number:
Console.WriteLine($"整数: {number}");
break;

case double d:
Console.WriteLine($"小数: {d}");
break;

case null:
Console.WriteLine("nullです");
break;

default:
Console.WriteLine("その他の型です");
break;
}

switch式を使うと、値を返す形で簡潔に書けます。

C#
object value = 123;

string message = value switch
{
string text => $"文字列: {text}",
int number => $"整数: {number}",
double d => $"小数: {d}",
null => "nullです",
_ => "その他の型です"
};

Console.WriteLine(message);

型ごとに処理を分岐する場合、無理にキャストを並べるよりも、switchとパターンマッチングを使ったほうが読みやすくなります。

6. Convertクラス・Parse・TryParseの違い

C#で型変換を行う方法として、キャスト以外にConvertParseTryParseがあります。

特に、文字列から数値へ変換する場合は、キャストではなくこれらの方法を使います。

6-1. Convert.ToInt32・Convert.ToDoubleなどの基本

Convertクラスには、基本型へ変換するためのメソッドが用意されています。

C#
string text = "123";
int number = Convert.ToInt32(text);

Console.WriteLine(number); // 123

doubleに変換する場合は、Convert.ToDoubleを使います。

C#
string text = "3.14";
double value = Convert.ToDouble(text);

Console.WriteLine(value); // 3.14

文字列だけでなく、数値型同士の変換にも使えます。

C#
double value = 123.45;
int number = Convert.ToInt32(value);

Console.WriteLine(number);

真偽値への変換もできます。

C#
string text = "true";
bool flag = Convert.ToBoolean(text);

Console.WriteLine(flag); // True

6-2. Convertとキャスト演算子の違い

Convertとキャスト演算子は、どちらも型変換に使えますが、目的が異なります。

キャスト演算子は、型同士の変換関係に基づいて値を変換します。

C#
double value = 123.45;
int number = (int)value;

Console.WriteLine(number); // 123

この場合、小数部分は切り捨てられます。

一方、Convert.ToInt32は変換処理をメソッドとして実行します。

C#
double value = 123.45;
int number = Convert.ToInt32(value);

Console.WriteLine(number); // 123

値によっては、キャストとConvertで結果が異なることがあります。

C#
double value = 123.6;

int castValue = (int)value;
int convertValue = Convert.ToInt32(value);

Console.WriteLine(castValue); // 123
Console.WriteLine(convertValue); // 124

キャストは小数部分を切り捨てますが、Convert.ToInt32は丸め処理を行います。

また、stringからintへの変換はキャストではできません。

C#
string text = "123";

// コンパイルエラー
// int number = (int)text;

この場合は、ConvertParseを使います。

C#
int number1 = Convert.ToInt32(text);
int number2 = int.Parse(text);

6-3. stringから数値へ変換するならParse・TryParse

文字列から数値へ変換する場合、よく使われるのはParseTryParseです。

C#
string text = "123";

int number = int.Parse(text);

Console.WriteLine(number); // 123

doubleの場合はdouble.Parseを使います。

C#
string text = "3.14";

double value = double.Parse(text);

Console.WriteLine(value); // 3.14

ただし、文字列が必ず正しい形式であると分かっている場合を除き、ParseよりもTryParseのほうが安全です。

C#
string text = "123";

if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("変換できません");
}

ユーザー入力を扱う場合は、入力ミスが起きる可能性があります。そのため、TryParseを使うのが基本です。

6-4. Parseで発生しやすいFormatException

Parseは、文字列の形式が正しくない場合にFormatExceptionを発生させます。

C#
string text = "abc";

// FormatException
int number = int.Parse(text);

"abc"は整数として解釈できないため、エラーになります。

空文字でもエラーになります。

C#
string text = "";

// FormatException
int number = int.Parse(text);

また、数値に見えても形式によっては失敗します。

C#
string text = "123abc";

// FormatException
int number = int.Parse(text);

Parseは「変換できることが確実な文字列」に対して使うのが基本です。

6-5. 変換失敗に強いTryParseの使い方

TryParseは、変換に成功したかどうかをboolで返します。変換結果はout引数で受け取ります。

C#
string text = "123";

bool success = int.TryParse(text, out int number);

if (success)
{
Console.WriteLine($"変換成功: {number}");
}
else
{
Console.WriteLine("変換失敗");
}

変換に失敗しても例外は発生しません。

C#
string text = "abc";

if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値ではありません");
}

ユーザー入力のチェックにも向いています。

C#
Console.WriteLine("年齢を入力してください");
string input = Console.ReadLine();

if (int.TryParse(input, out int age))
{
Console.WriteLine($"年齢は{age}歳です");
}
else
{
Console.WriteLine("正しい数値を入力してください");
}

このように、変換できない可能性があるデータにはTryParseを使うと安全です。

6-6. nullを変換するときのConvertとParseの違い

ConvertParseは、nullを扱うときの挙動が異なります。

C#
string text = null;

int number = Convert.ToInt32(text);

Console.WriteLine(number); // 0

Convert.ToInt32(null)0を返します。

一方、int.Parse(null)は例外になります。

C#
string text = null;

// ArgumentNullException
int number = int.Parse(text);

文字列へ変換する場合も違いがあります。

C#
object obj = null;

string text1 = Convert.ToString(obj);
string text2 = obj.ToString(); // NullReferenceException

Convert.ToString(null)は空文字を返しますが、null.ToString()は呼び出せないためNullReferenceExceptionになります。

nullの可能性がある値を扱う場合は、Convertやnullチェックを使って安全に処理しましょう。

7. C#のキャストでよくあるエラーと対策

C#のキャストや型変換では、いくつかの代表的なエラーが発生します。

エラーの意味を理解しておくと、原因を見つけやすくなります。

7-1. InvalidCastException:変換できない型をキャストした

InvalidCastExceptionは、キャストできない型に変換しようとしたときに発生します。

C#
object obj = 123;

// InvalidCastException
string text = (string)obj;

objの実体はintなので、stringにキャストできません。

対策としては、isで確認してから使います。

C#
object obj = 123;

if (obj is string text)
{
Console.WriteLine(text);
}
else
{
Console.WriteLine("stringではありません");
}

文字列に変換したいだけなら、キャストではなくToStringを使います。

C#
object obj = 123;
string text = obj.ToString();

Console.WriteLine(text); // "123"

7-2. FormatException:文字列の形式が正しくない

FormatExceptionは、文字列を数値や日付などに変換しようとしたとき、形式が正しくない場合に発生します。

C#
string text = "abc";

// FormatException
int number = int.Parse(text);

対策としては、TryParseを使います。

C#
string text = "abc";

if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値に変換できません");
}

ユーザー入力や外部データは、常に正しい形式とは限りません。そのため、ParseよりもTryParseを優先すると安全です。

7-3. OverflowException:変換先の型に値が収まらない

OverflowExceptionは、変換先の型の範囲を超える値を変換しようとしたときに発生します。

C#
string text = "999999999999999999999";

// OverflowException
int number = int.Parse(text);

intに収まらない大きな値なので、例外が発生します。

Convertでも同様に発生することがあります。

C#
long bigNumber = 9999999999;

// OverflowException
int number = Convert.ToInt32(bigNumber);

対策としては、より大きな型を使うか、TryParseで確認します。

C#
string text = "999999999999999999999";

if (long.TryParse(text, out long number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("long型にも収まりません");
}

また、範囲チェックを行うことも重要です。

C#
long value = 9999999999;

if (value >= int.MinValue && value <= int.MaxValue)
{
int number = (int)value;
Console.WriteLine(number);
}
else
{
Console.WriteLine("int型の範囲外です");
}

7-4. NullReferenceException:asの結果がnullだった

asを使った変換に失敗すると、結果はnullになります。

C#
object obj = 123;

string text = obj as string;

// NullReferenceException
Console.WriteLine(text.Length);

textnullなので、LengthにアクセスするとNullReferenceExceptionが発生します。

対策として、asの後は必ずnullチェックを行います。

C#
object obj = 123;

string text = obj as string;

if (text != null)
{
Console.WriteLine(text.Length);
}
else
{
Console.WriteLine("string型ではありません");
}

また、isパターンを使うと、より安全に書けます。

C#
object obj = 123;

if (obj is string text)
{
Console.WriteLine(text.Length);
}
else
{
Console.WriteLine("string型ではありません");
}

7-5. コンパイルエラー「明示的な変換が存在します」

C#では、暗黙的に変換できない型を代入しようとすると、コンパイルエラーになります。

C#
double value = 123.45;

// コンパイルエラー
// int number = value;

これは、doubleからintへの変換で小数部分が失われる可能性があるためです。

明示的なキャストを書くとコンパイルできます。

C#
double value = 123.45;
int number = (int)value;

Console.WriteLine(number); // 123

ただし、キャストすれば何でも安全になるわけではありません。データ損失が起きることを理解したうえで使う必要があります。

7-6. エラーを防ぐためのチェックリスト

C#のキャストや型変換でエラーを防ぐには、次の点を確認しましょう。

変換元と変換先の型に正しい関係があるか確認します。

C#
object obj = 123;

// stringではなくintとして取り出す
if (obj is int number)
{
Console.WriteLine(number);
}

文字列から数値に変換する場合は、TryParseを使います。

C#
if (int.TryParse("123", out int number))
{
Console.WriteLine(number);
}

asを使った後は、必ずnullチェックをします。

C#
string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

小数から整数への変換では、切り捨てや丸めの違いを意識します。

C#
double value = 3.9;

int a = (int)value;
int b = Convert.ToInt32(value);
int c = (int)Math.Round(value);

変換先の型に値が収まるか確認します。

C#
long value = 9999999999;

if (value <= int.MaxValue && value >= int.MinValue)
{
int number = (int)value;
}

8. 実践例で理解するC#のキャスト

ここからは、実際によくあるケースを使ってC#のキャストを確認します。

8-1. intからdoubleへ変換する例

intからdoubleへの変換は暗黙的に行えます。

C#
int number = 100;
double value = number;

Console.WriteLine(value); // 100

doubleは小数を扱えるため、intの値を問題なく受け取れます。

明示的にキャストしても動作しますが、通常は不要です。

C#
int number = 100;
double value = (double)number;

Console.WriteLine(value);

8-2. doubleからintへ変換する例

doubleからintへの変換では、明示的なキャストが必要です。

C#
double value = 123.45;
int number = (int)value;

Console.WriteLine(number); // 123

小数部分は切り捨てられます。

四捨五入したい場合は、Math.Roundを使います。

C#
double value = 123.45;
int number = (int)Math.Round(value);

Console.WriteLine(number);

Convert.ToInt32を使う方法もあります。

C#
double value = 123.45;
int number = Convert.ToInt32(value);

Console.WriteLine(number);

ただし、キャストとConvert.ToInt32では丸め方が異なる場合があるため、意図に合わせて選びましょう。

8-3. stringからintへ変換する例

stringからintへ変換する場合、キャストは使えません。

C#
string text = "123";

// コンパイルエラー
// int number = (int)text;

Parseを使うと変換できます。

C#
string text = "123";
int number = int.Parse(text);

Console.WriteLine(number); // 123

ただし、文字列が正しい数値とは限らない場合はTryParseを使います。

C#
string text = "123";

if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("変換できません");
}

ユーザー入力では、TryParseを使うのが安全です。

C#
Console.WriteLine("数字を入力してください");
string input = Console.ReadLine();

if (int.TryParse(input, out int number))
{
Console.WriteLine($"入力された数値: {number}");
}
else
{
Console.WriteLine("数値ではありません");
}

8-4. objectからstringへ変換する例

objectに文字列が入っている場合は、キャストでstringに戻せます。

C#
object obj = "Hello";
string text = (string)obj;

Console.WriteLine(text); // Hello

安全に変換したい場合は、asを使います。

C#
object obj = "Hello";

string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

さらに、isパターンを使うと、型判定と代入を同時に行えます。

C#
object obj = "Hello";

if (obj is string text)
{
Console.WriteLine(text);
}

objectの中身が文字列ではない場合も安全です。

C#
object obj = 123;

if (obj is string text)
{
Console.WriteLine(text);
}
else
{
Console.WriteLine("stringではありません");
}

8-5. 基底クラスから派生クラスへ変換する例

基底クラスから派生クラスへ変換する場合は、実体がその派生クラスである必要があります。

C#
class Animal
{
}

class Dog : Animal
{
public void Bark()
{
Console.WriteLine("ワン");
}
}

実体がDogであれば、ダウンキャストできます。

C#
Animal animal = new Dog();

Dog dog = (Dog)animal;
dog.Bark();

ただし、実体がDogでない場合は失敗します。

C#
Animal animal = new Animal();

// InvalidCastException
Dog dog = (Dog)animal;

安全に処理するなら、isパターンを使います。

C#
Animal animal = new Dog();

if (animal is Dog dog)
{
dog.Bark();
}
else
{
Console.WriteLine("Dogではありません");
}

8-6. Listや配列で型変換する例

配列やListの要素を別の型に変換したい場合は、LINQのCastOfTypeSelectを使うことがあります。

C#
using System;
using System.Collections.Generic;
using System.Linq;

List<object> values = new List<object>
{
"Apple",
"Banana",
"Orange"
};

List<string> texts = values.Cast<string>().ToList();

foreach (string text in texts)
{
Console.WriteLine(text);
}

Cast<string>()は、すべての要素をstringにキャストします。ただし、キャストできない要素があると例外が発生します。

C#
List<object> values = new List<object>
{
"Apple",
123,
"Orange"
};

// InvalidCastException
List<string> texts = values.Cast<string>().ToList();

キャストできる要素だけを取り出したい場合は、OfTypeを使います。

C#
List<object> values = new List<object>
{
"Apple",
123,
"Orange"
};

List<string> texts = values.OfType<string>().ToList();

foreach (string text in texts)
{
Console.WriteLine(text);
}

出力されるのはstringの要素だけです。

C#
Apple
Orange

数値のリストを別の数値型に変換したい場合は、Selectを使います。

C#
List<int> numbers = new List<int> { 1, 2, 3 };

List<double> doubles = numbers
.Select(n => (double)n)
.ToList();

文字列のリストを数値に変換する場合も、SelectParseTryParseを組み合わせます。

C#
List<string> texts = new List<string> { "1", "2", "3" };

List<int> numbers = texts
.Select(text => int.Parse(text))
.ToList();

変換できない文字列が混ざる可能性があるなら、TryParseを使って安全に処理しましょう。

C#
List<string> texts = new List<string> { "1", "abc", "3" };

List<int> numbers = texts
.Where(text => int.TryParse(text, out _))
.Select(text => int.Parse(text))
.ToList();

9. キャスト方法の使い分け早見表

C#のキャストや型変換は、目的に応じて使い分けることが大切です。

目的主な方法特徴
数値型同士を変換する(int)valueConvert.ToInt32切り捨てや丸めの違いに注意
文字列を数値に変換するParseTryParseConvert入力値が不確実ならTryParse
参照型を変換する(型)asis失敗時の挙動が異なる
型を判定して使うisパターン安全で読みやすい
文字列に変換するToStringConvert.ToStringnullに注意

9-1. 数値型同士の変換で使う方法

数値型同士の変換では、暗黙的な型変換、キャスト、Convertを使います。

intからdoubleのように安全な変換は暗黙的にできます。

C#
int number = 100;
double value = number;

doubleからintのようにデータが失われる可能性がある変換はキャストが必要です。

C#
double value = 123.45;
int number = (int)value;

丸めたい場合は、Convert.ToInt32Math.Roundを検討します。

C#
double value = 123.6;

int a = (int)value;
int b = Convert.ToInt32(value);
int c = (int)Math.Round(value);

9-2. 文字列から数値へ変換するときの方法

文字列から数値へ変換する場合は、キャストではなくParseTryParseを使います。

C#
string text = "123";
int number = int.Parse(text);

入力値が不確実な場合はTryParseを使います。

C#
string text = "123";

if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("変換できません");
}

Convert.ToInt32も使えます。

C#
string text = "123";
int number = Convert.ToInt32(text);

ただし、nullの扱いや例外の違いがあるため、ユーザー入力ではTryParseが最も扱いやすいです。

9-3. 参照型の安全な変換で使う方法

参照型の変換では、キャスト演算子、asisを使います。

成功することが分かっている場合はキャスト演算子を使えます。

C#
object obj = "Hello";
string text = (string)obj;

失敗時にnullとして扱いたい場合はasを使います。

C#
string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

型判定と代入を同時に行いたい場合はisパターンが便利です。

C#
if (obj is string text)
{
Console.WriteLine(text);
}

多くの場合、初心者にはisパターンが分かりやすく、安全です。

9-4. nullや例外を避けたいときの方法

例外を避けたい場合は、失敗する可能性がある処理に対して安全な方法を選びます。

文字列から数値ならTryParseです。

C#
if (int.TryParse(input, out int number))
{
Console.WriteLine(number);
}

参照型の変換ならisパターンです。

C#
if (obj is string text)
{
Console.WriteLine(text);
}

asを使う場合は、必ずnullチェックします。

C#
string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

ToStringを使う場合、対象がnullでないか確認します。

C#
object obj = null;

string text = obj?.ToString();

または、Convert.ToStringを使います。

C#
string text = Convert.ToString(obj);

9-5. 初心者が迷ったときの選び方

初心者がC#のキャストで迷ったときは、次のように考えると選びやすくなります。

数値型同士の変換なら、まず暗黙的に代入できるか確認します。できない場合はキャストを使います。

C#
double value = 123.45;
int number = (int)value;

文字列から数値にしたいなら、TryParseを使います。

C#
if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}

objectや基底クラスから特定の型として使いたいなら、isパターンを使います。

C#
if (obj is string text)
{
Console.WriteLine(text);
}

文字列に変換したいなら、ToStringまたはConvert.ToStringを使います。

C#
string text = value.ToString();

nullの可能性があるなら、Convert.ToStringやnull条件演算子を検討します。

C#
string text = obj?.ToString();

10. C#のキャストを使うときの注意点

キャストは便利ですが、多用しすぎるとエラーの原因になったり、コードの可読性を下げたりします。

C#のキャストを安全に使うための注意点を確認しましょう。

10-1. 無理なキャストは避ける

キャストは、変換できる型同士でなければ使えません。

C#
string text = "123";

// これはできない
// int number = (int)text;

文字列の内容を数値として解釈したい場合は、キャストではなくParseTryParseを使います。

C#
if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}

また、object型だからといって、どの型にも自由にキャストできるわけではありません。

C#
object obj = 123;

// InvalidCastException
// string text = (string)obj;

実際の型を確認してから変換しましょう。

C#
if (obj is int number)
{
Console.WriteLine(number);
}

10-2. データ損失が起きる変換に注意する

数値型のキャストでは、データ損失が起きることがあります。

C#
double value = 123.99;
int number = (int)value;

Console.WriteLine(number); // 123

小数部分は失われます。

また、大きな型から小さな型へ変換する場合、値が収まらない可能性があります。

C#
long bigValue = 9999999999;

// intの範囲を超える
// int number = (int)bigValue;

安全に変換するには、範囲チェックを行います。

C#
long bigValue = 9999999999;

if (bigValue >= int.MinValue && bigValue <= int.MaxValue)
{
int number = (int)bigValue;
Console.WriteLine(number);
}
else
{
Console.WriteLine("int型に収まりません");
}

10-3. 例外処理に頼りすぎない

キャストや変換に失敗する可能性がある場合、毎回try-catchで処理するよりも、事前にチェックするほうが読みやすくなります。

たとえば、文字列から数値への変換で、次のようにParsetry-catchを使うこともできます。

C#
try
{
int number = int.Parse(input);
Console.WriteLine(number);
}
catch (FormatException)
{
Console.WriteLine("数値ではありません");
}

しかし、入力チェックが目的ならTryParseのほうが適しています。

C#
if (int.TryParse(input, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値ではありません");
}

型変換でも同じです。例外が発生してから対応するよりも、isで確認してから処理するほうが安全です。

C#
if (obj is string text)
{
Console.WriteLine(text);
}

10-4. 可読性を下げるキャストを増やさない

キャストが多いコードは、型の流れが分かりにくくなります。

C#
object value = GetValue();

int number = (int)(double)value;

このようなコードは、なぜその型変換が必要なのか分かりにくく、実行時エラーの原因にもなります。

キャストが多い場合は、変数の型やメソッドの戻り値の型を見直しましょう。

C#
double value = GetDoubleValue();
int number = (int)value;

最初から適切な型を使えば、不要なキャストを減らせます。

また、object型を多用すると、後からキャストが必要になりやすいです。

C#
object name = "Taro";
object age = 20;

可能であれば、具体的な型を使いましょう。

C#
string name = "Taro";
int age = 20;

10-5. 型設計を見直したほうがよいケース

キャストが頻繁に必要になる場合、型設計に問題があるかもしれません。

たとえば、毎回objectから特定の型へキャストしている場合です。

C#
object value = GetValue();

if (value is string text)
{
Console.WriteLine(text);
}

本当にobjectで受け取る必要があるのか、戻り値の型を具体的にできないかを検討しましょう。

C#
string value = GetText();
Console.WriteLine(value);

また、基底クラスから派生クラスへのダウンキャストが多い場合も注意が必要です。

C#
Animal animal = GetAnimal();

if (animal is Dog dog)
{
dog.Bark();
}

このような処理があちこちにある場合、ポリモーフィズムを使って設計を改善できる可能性があります。

C#
abstract class Animal
{
public abstract void Speak();
}

class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワン");
}
}

class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("ニャー");
}
}

こうすれば、型ごとにキャストしなくても処理できます。

C#
Animal animal = new Dog();
animal.Speak();

キャストは便利ですが、頻繁に必要になる場合は「型の設計を見直すサイン」と考えるとよいでしょう。

まとめ

C#のキャストは、ある値を別の型として扱うための重要な機能です。

基本となるキャスト演算子(型)は、数値型同士の変換や、objectから元の型への変換、クラスのアップキャスト・ダウンキャストなどで使います。

C#
double value = 123.45;
int number = (int)value;

ただし、キャストに失敗するとInvalidCastExceptionが発生するため、変換できるか分からない場合はisasを使って安全に処理します。

C#
if (obj is string text)
{
Console.WriteLine(text);
}

asは失敗時にnullを返すため、使用後のnullチェックが必要です。

C#
string text = obj as string;

if (text != null)
{
Console.WriteLine(text);
}

文字列から数値に変換する場合は、キャストではなくParseTryParseConvertを使います。

C#
if (int.TryParse(text, out int number))
{
Console.WriteLine(number);
}

特に、ユーザー入力や外部データのように変換できるか分からない値には、TryParseが安全です。

C#のキャストで大切なのは、「どの方法でも同じ」と考えないことです。

数値型同士の変換なのか、文字列から数値への変換なのか、参照型の変換なのか、失敗時に例外を出したいのか、nullfalseで判定したいのかによって、適切な方法は変わります。

初心者のうちは、次の基準で使い分けると分かりやすいです。

やりたいこと使う方法
数値型同士を変換したいキャスト、Convert
小数を整数にしたいキャスト、Math.Roundなど
文字列を数値にしたいTryParse、Parse、Convert
objectから特定の型として取り出したいisパターン、as、キャスト
型を確認してから使いたいisパターン
文字列にしたいToString、Convert.ToString

C#のキャストを正しく理解すると、型変換のエラーを減らし、より安全で読みやすいコードを書けるようになります。