【ゲーム開発のためのC#入門講座・基礎強化編】nullに注意しよう【#7】
ぬるぽって聞いたことありますか?
一昔前のネット用語「ぬるぽ」をご存じですか?
シュタインズ・ゲートで知った、なんて若い方もいるかもしれませんね。
この「ぬるぽ」はJavaという言語で発生するエラーNullPointerException
が語源のネット用語です。「よく見かけるエラーで、対応が面倒な憎いやつ」であることから、せめて可愛らしく言おうぜという経緯から生まれたとかなんとか。
C#の場合は同じ原因のエラーNullReferenceException
が、これまた 「よく見かけるエラーで、対応が面倒な憎いやつ」 として今後皆さんの前に度々登場することになります。
名前から「Null (ヌル) を参照(Reference)してエラー(Exception)になった」らしいことは何となく察せますが、この「Null(ヌル)」とはいったい何なのでしょうか?
空っぽの参照型には何が入っている?
参照型は型情報だけではメモリサイズがわからないことから、new
という命令実行時に初めてヒープメモリにデータが作成されるのでしたね。そしてそのアドレスはスタックメモリに保存されるのでした。
それでは、下記プログラムのint
型配列変数A
には何が入っていると思いますか?
public class Hello{
public static void Main(){
int[] A;
}
}
空っぽの参照型には何が入っている? と聞くとちょっと哲学的にも思えますね。
結論からいえば、何も代入していないのだから何も入っていません。当然ですね。
では、この何も入っていない変数を参照して、何か処理を実行しようとするとどうなるでしょう?
参照型ですから、まずはスタックメモリに格納されているヒープメモリのアドレスを参照しようとしますよね。でもメモリは初期状態のままなので、実際にどこかにある実データの場所を指し示している訳ではありません。
結果、存在しないデータを参照しようとしてエラーが発生します。
このように、実データを参照することができない無効なアドレスが設定されている状態、つまり参照型の変数が空っぽの状態のことをnullといいます。
nullのデータを参照しようとすると、先程ご紹介したNullReferenceException
というエラーが発生してしまうので、注意が必要です。
どういう時に注意すればいいの?
このnullで一番注意しなければいけないポイント、それは戻り値です。
例えば下記のようなプログラムがあったとします。
public class Hello{
public static void Main(){
string[] names = {
"He",
"Ro"
};
string name = FindHero(names);
System.Console.WriteLine(name.Length);
}
public static string FindHero(string[] names)
{
for (int i = 0; i < names.Length; i++)
{
if (names[i] == "Hero")
{
return names[i];
}
}
return null;
}
}
関数FindHero
は、引数のstring
型配列の中にHero
という文字列が存在する場合はその文字列を、存在しない場合はnull
を返すというものです。
関数というのは、戻り値の型を指定した場合、必ず何らかの値を返さなければならないという決まりがあります。
そのため、上記のような「~~を検索する」系の関数の場合、該当のデータを見つけられなかった時はデータが存在しないことを意味するnullを返すということになります(実際にはnull
以外を返す方法もあるのですが、一昔前までは他に有効な選択肢がなく、null
を返すことが多かったです)。
結果、必ず有効なデータが返されるものだと思い込んでしまうと、戻り値を使った処理を実行しようとした時にNullReferenceException
に遭遇してしまうという訳です。
実際に実行してみると、下記のようにエラーが出てしまいます。
出力エリアUnhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object at Hello.Main () [0x0001f] in /workspace/Main.cs:8
[ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object at Hello.Main () [0x0001f] in /workspace/Main.cs:8
nullかどうか事前にチェックしよう
対処法にはいくつか選択肢があるのですが、一番わかりやすいのは事前にnull
かどうかチェックするというものです。
これは比較演算子を使って実現することができます。
試しに先程のプログラムを、エラーが発生しないよう変更してみましょう。
public class Hello{
public static void Main(){
string[] names = {
"He",
"Ro"
};
string name = FindHero(names);
// ↓ここで事前にチェックしてるよ!
if (name != null)
{
System.Console.WriteLine(name.Length);
}
else
{
System.Console.WriteLine("nullだったよ!");
}
}
public static string FindHero(string[] names)
{
for (int i = 0; i < names.Length; i++)
{
if (names[i] == "Hero")
{
return names[i];
}
}
return null;
}
}
出力エリアnullだったよ!
無事、変数がnull
であってもエラーを発生させずに処理を終えることができました。
戻り値としてnull
が返される可能性があるならば、このように事前にチェックした上で利用するよう気を付けた方がよいでしょう。
Unityでの活躍ポイント
実はUnityで頻繁に発生しうるエラーがNullReferenceException
です。
なぜならば、Unityはキャラクターやマップに配置したオブジェクトなど、大量の参照型データを扱う機会が非常に多いからです。そしてその検索用に用意された関数では、該当するデータが見当たらなかった場合にnull
が返されるようになっています。
結果、こんな風に超膨大な量のNullReferenceException
が発生することも珍しくありません。
null
とは何なのか、そしてそれに対処するにはどうしたらよいのかを学ぶことで、スムーズに対応ができるようになります。
忘れないようにしておきましょう!
実践演習
それでは実際にnull
参照によるエラーを回避してみましょう。
演習①nullじゃない時だけ処理を実行しよう
仕様下記のプログラムはエラーが発生してしまいます。エラーが発生しないよう、関数
CountLength
にて、引数がnull
じゃなかった時だけ加算処理を実行するようにしてください。
テンプレートpublic class Hello{ public static void Main(){ string text = null; System.Console.WriteLine(CountLength(text, "hoge")); } public static int CountLength(string text, string text2) { int count = 0; count += text.Length; count += text2.Length; return count; } }
演習②nullだったら別の値を返すようにしよう
仕様下記のプログラムはエラーが発生してしまいます。エラーが発生しないよう、関数
ReplaceNull
にて、ひとつ目の引数がnull
だった場合、代わりにふたつ目の引数を戻り値として返してください。
テンプレートpublic class Hello{ public static void Main(){ string text = null; text = ReplaceNull(text, "代替テキスト"); System.Console.WriteLine(text); } public static string ReplaceNull(string text, string altText) { // ↓ここにひとつ目の引数がnullだった場合の処理を実装しよう! // ↑ここまで return text; } }
答え合わせ
演習①の答え
public class Hello{
public static void Main(){
string text = null;
System.Console.WriteLine(CountLength(text, "hoge"));
}
public static int CountLength(string text, string text2)
{
int count = 0;
if (text != null)
{
count += text.Length;
}
if (text2 != null)
{
count += text2.Length;
}
return count;
}
}
演習②の答え
public class Hello{
public static void Main(){
string text = null;
text = ReplaceNull(text, "代替テキスト");
System.Console.WriteLine(text);
}
public static string ReplaceNull(string text, string altText)
{
// ↓ここにひとつ目の引数がnullだった場合の処理を実装しよう!
if (text == null)
{
return altText;
}
// ↑ここまで
return text;
}
}
まとめ
null
とは、実データを参照することができない、変数が空っぽの状態のこと- 関数(特に検索系)の戻り値で使われることが多いので、戻り値を使った処理での
NullReferenceException
に注意 - 事前に
null
チェックを行うなど、対応策を忘れないようにしておきましょう
それでは、今回もお疲れ様でした!
また次の記事でお会いしましょう!
お借りした素材一覧
この記事では下記サイト様の素材をお借りしています。
ありがとうございました!