いろいろガンガンいこうぜ。体も大事に

普段はJavaでAndroidアプリ開発しているプログラマーのブログです。

StopCoroutineみたいに引数にメソッド名を文字列で渡すのが嫌。

前置き

静的型付け言語の良さの一つは,実行する前にコンパイルエラーで型の矛盾点やおかしい点を見つけられることだと思います。 例えば,定義していないメソッドを呼び出そうとしたり,クラス名が間違っていた場合,実行前におかしいということに気づけます。 EclipseやInteliJ,MonoDevelopなどのIDEもコンパイルが通らないコードを書いたら,「おい,そこおかしいぞ」って教えてくれます。

StartCoroutineメソッドとStopCoroutineメソッド

話は変わって,UnityのMonoBehaviorクラスのStartCoroutineメソッドとStopCoroutineメソッド。 1秒をおきに"logging"ってデバックログを表示したいとします。

private IEnumerator RoopLoggingRoutine ()
{
    while (true){
        yield return new WaitForSeconds (1.0F);
        Debug.Log ("logging");
    }
}

というメソッドを定義して, これを,

StartCoroutine (RoopLoggingRoutine ());

もしくは,

StartCoroutine ("RoopLoggingRoutine");

って呼び出してあげれば良いです。

ロギングをやめたい場合は,StopCoroutineメソッドを用います。

StopCoroutine ("RoopLoggingRoutine");

StopCoroutineは文字列を引数にとるものしかありません。 また,StopCoroutineメソッドで止めることができるのは,StartCoroutineメソッドの文字列を引数にとるオーバーロードで始めたコルーチンだけです。

IEnumeratorを引数にとるコルーチンを能動的に止めるには,StopAllCoroutinesメソッドを用いる必要があります。

メソッド名の文字列の引数が嫌だ。

やっと,本題。

メソッド名を変更する,そんな状況を想像してください。 普通だったらIDEのリファクタリング機能を使って,変更したメソッドを呼び出している箇所も一気に変更すると思います。 仮にそうしなくても古いメソッド名のままになっている箇所は,コンパイルエラーになるので,それをIDEが教えてくれます。

しかし,StartCoroutineやStopCoroutineのようにメソッド名を文字列で引数で渡している場合そうはなりません。

前の例のRoopLoggingRoutineをLoggingRoutineというメソッド名に変更するとします。

StopCoroutine ("RoopLoggingRoutine")StartCoroutine ("RoopLoggingRoutine")という箇所も変更しないといけません。 この箇所はリファクタリング機能では変更されません。自分で変更しなくてはいけません。もし,この箇所の変更を忘れた場合,IDEは何も警告をしてくれませんし,コンパイルも通ります。

このまま変更を忘れてまま,実行すると次のようなエラーが出てしまいます。

Coroutine 'RoopLoggingRoutine' couldn't be started!
UnityEngine.MonoBehaviour:StartCoroutine(String)

実行時にエラーに気づくのでなく,最悪でもコンパイル時・ビルド時にエラーに気づけるようにしたいです。

対処法

System.Funcクラスを使います。

StopCoroutine ("RoopLoggingRoutine");

string methodName = ((Func<IEnumerator>) RoopLoggingRoutine).Method.Name;
StartCoroutine (methodName);

と変更します。

こうすれば,リファクタリング機能を用いてRoopLoggingRoutineというメソッド名をLoggingRoutineに変えた際にコルーチン処理の箇所も変更されます。もしリファクタリング機能を用いずに,変えた場合もコンパイル時にエラーになったり,MonoDevelopが警告してくれるので,実行時に「なんかエラーでた!なんだこれ!?」と焦ることもないです。

まとめ

System.Funcを介して,メソッド名を取得する方法でコルーチン処理を管理すれば,実行時エラーをさけることができると思います。 ブログを書いておいてあれなのですが,Func<IEnumerator>でキャストしてるところで何が起きているのか,自分はしっかりと分かっていないです。勉強しておきます。

最後にもう一つ。 Unityのスクリプトリファレンスによると,そもそも文字列を引数にとるStartCoroutineメソッドの呼び出しはオーバーヘッドが大きいそうです。またパラメータも一つしかとれません。その点にご注意ください。

以上です。

参考

Unityスクリプトリファレンス StartCoroutine

Unityスクリプトリファレンス StopCoroutine

関連

サンプルプロジェクト (github)