ホームに戻る
出典 :
関連 :
目次 :
typeof 演算子による型取得
typeof 演算子を用いることで、型から System.Type インスタンスを得ることができる。
これにより、型の比較・判別を行うことができる。
またオブジェクト(クラス)型インスタンスは GetType() メソッドにより、自身の型に対応する Type インスタンスを取得できる。
class Base {}
class Level1 : Base{}
class Level2 : Level1{}
class Level3 : Level2{}
class Test
{
static void Main()
{
Base obj = new Level2();
bool isBase = obj.GetType() == typeof(Base); //< false
bool isLevel1 = obj.GetType() == typeof(Level1); //< false
bool isLevel2 = obj.GetType() == typeof(Level2); //< true
bool isLevel3 = obj.GetType() == typeof(Level3); //< false
}
}
typeof 演算子と == による比較は、型が厳格に一致する場合のみ真となる。
typeof(T) (Tは任意の型)は定数式ではないため、switch - case 節で用いることはできない。
型キャスト
オブジェクトの型を、指定した別の型に変換すること。
C#ではC/C++と同様の () によるキャスト以外に、as / is 演算子を使用できる。
アップキャストとダウンキャスト
派生クラスから基底クラスへの変換をアップキャスト、逆に、基底クラスから派生クラスへの変換をダウンキャストと呼ぶ。
アップキャスト
派生クラスから基底クラスへの型変換を指す。C#に限らず基底クラスは派生クラスをラップ可能であるため、明示的なキャストを行う必要はない。
class Base {}
class Derived : Base {}
class Test
{
static void Main()
{
Base a = null;
Derived b = new Derived();
a = b; //< アップキャスト
//< ( Base 型変数に Derived 型オブジェクトを格納 ⇒ 明示的キャスト不要)
}
}
ダウンキャスト
基底クラスから派生クラスへの型変換を指す。
派生クラスのインスタンスをアップキャストした場合のみダウンキャストが可能である。
( () 演算子でキャストを試みた場合、ダウンキャストできなければ例外( InvalidCastException )となる点に注意)
意図されたキャストであることを明示する必要がある。
class Base {}
class Derived1 : Base {}
class Derived2 : Base {}
class Test
{
static void Main()
{
Base a = new Derived1(); //< アップキャスト
Derived1 b = (Derived1)a; //< ダウンキャスト成功
Derived2 c = (Derived2)a; //< ダウンキャスト失敗 ⇒ 例外
}
}
上記の例では a は Derived1 型のオブジェクトであるが、基底クラスの Base 型にアップキャストされている。
ダウンキャストにより Derived1 型に戻すことは可能であるが、素性の異なる Derived2 型への変換は不可能である。
class Base {}
class Derived1 : Base {}
class Derived3 : Derived1 {}
class Test
{
static void Main()
{
Base a = new Derived3(); //< アップキャスト
Derived3 b = (Derived3)a; //< ダウンキャスト成功
Derived1 c = (Derived1)a; //< ダウンキャスト成功
}
}
上記の例では a は元々 Derived3 型のオブジェクトであるため、Derived3 型に戻すことも、1代前の基底クラス Derived1 型に変換することも可能である。
as 演算子
左辺のオブジェクトを右辺で指定された型に変換する。
using System;
class CastSample
{
public static void Main()
{
// オブジェクトを宣言
object nyanchu = "nyanchu"
// as によるキャスト : object ⇒ CastSample
// キャスト失敗、cs1 = null となる
CastSample cs1 = nyanchu as CastSample
Console.WriteLine(cs1 == null); // True
try
{
// () によるキャスト : object ⇒ CastSample
// キャスト失敗、例外が投げられる
CastSample cs2 = (CastSample)nyanchu;
}
catch(InvalidCastException e)
{
Console.WriteLine(e.ToString()); // System.InvalidCastException: 指定されたキャストは有効ではありません。 at CastSample.Main()
}
}
}
as と () の差異
キャストの失敗時、() を用いた場合は例外となるのに対し、as では式の評価結果が null となる。
() によるキャストは値型・参照型を問わず使用できるのに対し、as は参照型にしか用いることができない。
但し as によるキャストは () を用いる場合と比べて高速である(後述)。
// 値型に対する () を用いたキャスト ⇒ OK
int i = (int)1;
// 値型に対する as を用いたキャスト ⇒ NG(コンパイルエラー)
int i = 1 as int;
is 演算子
左辺のオブジェクトが右辺で指定された型に変換可能であるかを返す。右辺に新たな変数を指定した場合、変換可能であれば当該変数に変換後のオブジェクトが格納される。
後者の用法はパターンマッチングと呼ばれ、変換可否の確認と変数の初期化を一文で実行できる。C#7.0で追加された。
パターンマッチングの詳細はリンク先を参照。
class Base {}
class Level1 : Base{}
class Level2 : Level1{}
class Level3 : Level2{}
class Test
{
static void Main()
{
Base obj = new Level2();
bool isBase = (obj is Base); //< true ← typeof と異なる
bool isLevel1 = (obj is Level1); //< true ← typeof と異なる
bool isLevel2 = (obj is Level2); //< true
bool isLevel3 = (obj is Level3); //< false
}
}
また、is 演算子はオブジェクトがnullであるかをテストすることも可能である。
( is not で非nullをテストすることも可能。)
int i = 5;
PatternMatchingNullable(i);
int? j = null;
PatternMatchingNullable(j);
double d = 9.78654;
PatternMatchingNullable(d);
static void PatternMatchingNullable(ValueType? val)
{
// パターンマッチング : val に使用できるのはnull不許容型のみ
if (val is int j)
{
Console.WriteLine(j);
}
// val がnull許容型、かつnull(値無し)の場合
else if (val is null)
{
Console.WriteLine("val はnullです。");
}
else
{
Console.WriteLine("変換できません : " + val.ToString());
}
}
また、パターンマッチングの延長として、以下のような switch - case 構文も使用できる。
int i = 5;
PatternMatchingNullable(i);
int? j = null;
PatternMatchingNullable(j);
double d = 9.78654;
PatternMatchingNullable(d);
static void PatternMatchingSwitch(ValueType? val)
{
switch (val)
{
case int number:
Console.WriteLine(number);
break;
case long number:
Console.WriteLine(number);
break;
case decimal number:
Console.WriteLine(number);
break;
case float number:
Console.WriteLine(number);
break;
case double number:
Console.WriteLine(number);
break;
case null:
Console.WriteLine("val はnullです。");
break;
default:
Console.WriteLine("変換できません : " + val.ToString());
break;
}
}
() / as / is の速度比較
using System;
class CastBench
{
// サンプルクラス1
class Sample1
{
public int n = 1;
}
// サンプルクラス2
class Sample2
{
}
// () によるキャスト
private static int testCast(object [] testdata)
{
int sum = 0;
foreach (object o in testdata)
{
try
{
sum += ((Sample1)o).n;
}
catch (InvalidCastException)
{
sum += 1;
}
}
return sum;
}
// as によるキャスト
private static int testAs(object [] testdata)
{
int sum = 0;
foreach (object o in testdata)
{
Sample1 s = o as Sample1;
if (s != null)
{
sum += s.n;
}
else
{
sum += 1;
}
}
return sum;
}
// is によるテスト + () によるキャスト
private static int testIsCast(object [] testdata)
{
int sum = 0;
foreach (object o in testdata)
{
if (o is Sample1)
{
sum += ((Sample1)o).n;
}
else
{
sum += 1;
}
}
return sum;
}
// Main() メソッド
public static void Main()
{
// テストデータの作成
// 1000個に1個 Sample2 、それ以外は Sample1 を配置
object [] testdata = new object[1000000];
for (int i = 0; i < testdata.Length; i++)
{
if (i % 1000 != 0)
{
testdata[i] = new Sample1();
}
else
{
testdata[i] = new Sample2();
}
}
// () によるキャスト
DateTime startCast = DateTime.Now;
for (int i = 0; i < 1000; i++)
{
testCast(testdata);
}
DateTime endCast = DateTime.Now;
Console.WriteLine("キャストの実行時間 {0}", endCast - startCast);
// 出力例 : キャストの実行時間 00:00:47.9899632
// as によるキャスト
DateTime startAs = DateTime.Now;
for (int i = 0; i < 1000; i++)
{
testAs(testdata);
}
DateTime endAs = DateTime.Now;
Console.WriteLine("as演算子の実行時間 {0}", endAs - startAs);
// 出力例 : as演算子の実行時間 00:00:11.7771696
// is によるテスト + () によるキャスト
DateTime startIsCats = DateTime.Now;
for (int i = 0; i < 1000; i++)
{
testIsCast(testdata);
}
DateTime endIsCast = DateTime.Now;
Console.WriteLine("is演算子+キャストの実行時間 {0}", endIsCast - startIsCats);
// 出力例 : is演算子+キャストの実行時間 00:00:20.7702804
}
}
(処理時間は目安)
上記のコードでは、1,000,000個のデータ列に対する型変換を1,000回繰り返し行っている。ここで、データ列には1,000個に1個の割合で Sample2 が混入しており、Sample1 への変換を行おうとすると失敗する。
() によるキャストは、変換失敗時に例外が発生することもあって3つの中では最も所要時間が長い。(例外の送出はオーバーヘッドが大きい。)
変換の直前に is によるテストを行うと例外の発生はゼロになるため、速度は改善する。as を用いた場合は更に高速となる。
尚、例外が発生しないケースでも () より as が高速である。
先述のパターンマッチングはテストと変換(割り当て)が一文で行えることからコードがシンプルになることに加え、速度面で有利となるため、使用が推奨されている。