ここではC#のis演算子とas演算子について学びます。
この話はキャストが深く関係するため、以下を理解してる必要があります。
ここではC#の型変換、キャストについて学びます。キャスト (cast)キャストとは型変換のことです。ある型で定義された変数や値を別の型に変更することを言います。今の知識範囲だと全ての変換方法は必要ないので、最も基礎的な暗黙的キャストと明示的キャストを理解しましょう。暗黙的キャストこれが最もシンプルな方法です。まずはサンプルコードを見てみましょう。namespace Sample { internal class Program { ...
ここではC#の継承について学びます。これはクラスの延長線の話になりますが少し難しいです。まずはクラスの基礎知識が必要なので、忘れてしまった人は以下で復習しましょう。継承(inheritance)継承とは、あるクラスを元に新しいクラスを定義することです。こうすることで新しく定義したクラスは元になったクラスの全ての機能を有することができます。簡単に言えば親子関係です。親(A)を元に子(B)を定義することができます。人間なら全ての特性を引き継ぐことはありませんが、この世...
危険なキャスト
C#でキャストする最も簡単な方法は暗黙的キャストまたは明示的キャストです。
そして暗黙的キャストは問題ないとしても、明示的キャストには危険が伴います。
特に型変換ができない場合は例外が発生するため、結果としてプログラムが死にます。
以下は一般的なダウンキャストのサンプルコードです。これは明示的キャストのためビルドエラーにはなりません。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
// 明示的キャストでダウンキャスト
Child c = (Child)p; // ここで例外が発生する
}
}
}
こういった問題はtry-catchで捕捉してもいいのですが、処理を実行するかを判断したほうが速度的に有利です。
そんな時の条件分岐で利用するのが、今回のis演算子とas演算子になります。
is演算子
is演算子を利用すると、指定した型に変換が可能かどうかを判断できます。
結果は真偽値で返るので、この値をif文とかで判断すると条件分岐ができます。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
Console.WriteLine(p is Parent);
Console.WriteLine(p is Child);
Child c = new Child();
if (c is Child)
{
Console.WriteLine("型変換が可能です");
}
}
}
}
構文を抜き出すと以下です。
変数 is 型
型チェックと同時に代入
is演算子を利用する時に変数名を記述すると、型チェックと同時に代入ができます。
変換前の変数 is 型 変換後の変数
これとif文を併用することで、型変換が可能な場合だけ処理するような記述ができます。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
public void Method()
{
Console.WriteLine("Child.Method()");
}
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Child();
// 型変換が可能な場合は変数に代入される
if (p is Child c)
{
// 代入された変数をそのまま使える
c.Method();
}
}
}
}
これの注意点として、作成する変数のスコープはif文と同じランクになります。
つまり、見た目はfor文とかの変数宣言に似てますが、以下のような記述は同名のビルドエラーになります。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
public void Method()
{
Console.WriteLine("Child.Method()");
}
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Child();
if (p is Child c)
{
c.Method();
}
// 同スコープなので無理
var c = null;
}
}
}
否定形
一緒にnotを記述すると否定形の判断になります。つまり、変換できない場合の処理を記述できます。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
// notで否定形
if (p is not Child)
{
Console.WriteLine("変換できません");
}
}
}
}
これは普通にis演算子の結果を!演算子で反転した場合と等価です。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
if (!(p is Child))
{
Console.WriteLine("変換できません");
}
}
}
}
なお、見た目は天と地くらいの差があります。
notを使ったほうが無駄な括弧が無くて綺麗だね。
nullを判断する
is演算子はnullを判断することもできます。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = null;
if (p is null)
{
Console.WriteLine("p is null");
}
Child c = new Child();
if (c is not null)
{
Console.WriteLine("c is not null");
}
}
}
}
これif文の!=で書くのと何が違うんですか?
超大雑把に言うとis演算子のほうが早い。
C#は演算子をオーバーロードすることで==とか!=の機能を書き換えできます。
ここではC#のオーバーロードについて学びます。なお、似た名前にオーバーライドって機能がありましたが、両者は全く関係がありません。もちろん骸骨の異世界系アニメでもありません。分かるわ。オーバーロード(overload)オーバーロードとは、引数が異なるメソッドを同名で定義できる機能です。これは例を見たほうが早いので、次のサンプルコードを実行してください。namespace Sample { public class Calculation...
つまり、==や!=は処理を行った結果で両者が等しいか異なるかを判断してます。
対してis演算子のnull判断は、nullかどうかを判断してるので単純に早いです。
as演算子
as演算子は必ずキャストするけど駄目ならnullにするって演算子です。
つまり、とりあえずキャストしてみて型的にNGならnullを返します。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
Console.WriteLine(p as Parent); // p = not null
Console.WriteLine(p as Child); // p = null
}
}
}
構文を抜き出すと以下です。is演算子と変わりません。
変数 as 型
その特性から基本的に変数の代入と併用します。
namespace Sample
{
public class Parent
{
}
public class Child : Parent
{
}
internal class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
// 無理な型変換はnullになる
Child c = p as Child;
// nullの受け入れが可能な仕組みと併用すると記述が統一できる
Console.WriteLine(c);
}
}
}
これのメリットは処理の統一です。上記はConsole.WriteLine()に引数として値を渡しました。
このように受け入れ側でnullが許容される場合、例外処理などを使わずとも同一の記述ができます。
なお、as演算子は結果がnullの可能性があるため、参照型でしか利用できません。
よって、値型に対してas演算子を利用するとビルドエラーになります。
関数(※1)を学んだことで、ついに値型と参照型の違いを知ることができます。ここではC#で最重要とも言える値型と参照型の違いを理解しましょう。※1現在の知識を踏まえて、ここではメソッドではなく関数と呼びます。値型と参照型の復習まずは値型と参照型について復習しましょう。この2つは値を保存する場所が大きなポイントになります。値型値型とは用意した領域に直接的に値を保存します。参照型参照型は値を別の領域に格納し、それを...
あとがき
真面目にコーディングしてると明示的キャストはほぼ使いません。仕方なく利用することもありますが、基本的に不要と思ってください。
ただ、Error処理を例外で統一して、あえて明示的キャストで変換。駄目なら例外に飛ばすような方法とかは普通にあります。
理解して使うなら何してもヨシ!
◆ C#に関する学習コンテンツ
この記事は参考になりましたか?
コメント