[Unity初心者Tips]FindとGetComponentを実行しエラーにどこから呼び出されたかを出す便利メソッド

# なにをやるのか
Unityで、FindしたやつからGetComponentするという処理は、プログラムの設計によってシングルトンでやってないやつだとよくやります。それの便利版です。説明要らない方は最後へジャンプ。

```c#
GameObject gameObject = GameObject.Find("HOGEgo");
HogeComponent hogeComponent = gameObject.GetComponent<HogeComponent>();
```

# 親切にエラーを出してあげる
この時、それぞれでnullの可能性があるので、その場合にはエラーを吐いた方が~~うるさいゲームデザイナーの相手をしなくていい~~親切に何がおかしいか判った方が効率が好いので、以下のコードになります。


```c#
HogeComponent hogeComponent;
GameObject gameObject = GameObject.Find("HOGEgo");
if(gameObject == null){
  Debug.LogError("HOGEgo is not found");
}
else{
  hogeComponent = gameObject.GetComponent<HogeComponent>();
  if(gameObject == null){
    Debug.LogError("HogeComponent is not found");
  }
}
```
# 共用できるclassのメソッドにする
折角なので、適当なclassで何処でも使えるようにジェネリック化したstaticのメソッドにしましょう。

```c#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace CommonTools
{
    public class Tools : MonoBehaviour
    {
        public static T GetComponentInObject<T>(string objectName)
        {
            T c = default(T);
            GameObject gameObject = GameObject.Find(objectName);
            if (gameObject == null) {
                Debug.LogError(objectName + " is not found");
            }
            else {
                c = gameObject.GetComponent<T>();
                if (hogeComponent == null) {
                    Debug.LogError( nameof(T)+ " is not found");
                }
            }
            return c;
        }
    }
}
```
まあまあ良い感じですね！ここまではありがちです。

# どこでやったか判るようにする
上のままでも問題ないんですけど、**色々なところから呼び出されるのでエラーを見ても呼び出し元が判らないと修正に困る**という問題があります。
~~うるさいゲームデザイナーの相手をしなくていい~~親切に何がおかしいか判った方が効率が好いので、ここで呼び出し元が判るようにします。それには以下の仕組みを使います。
## 呼び出し元はstackから辿れる
アセンブラの知識がある方はご存知の通り、メソッドの呼び出し毎に戻りアドレスがpushされてstackに格納されて積まれます。そこを辿れば、どこから呼び出されているのか判るわけです。
参考：[StackFrame クラス
https://docs.microsoft.com/ja-jp/dotnet/api/system.diagnostics.stackframe?view=netcore-3.1]
(https://docs.microsoft.com/ja-jp/dotnet/api/system.diagnostics.stackframe?view=netcore-3.1)

## リフレクションで名前を取得する
C#のリフレクションを利用すると、呼び出し元の名前が取得できます。
参考：[MemberInfo.ReflectedType プロパティ
https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.memberinfo.reflectedtype?view=netcore-3.1](https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.memberinfo.reflectedtype?view=netcore-3.1)

## エラーで呼び出し元を出す完成版
上記を組み合わせてエラーのためのメソッドを`Find`と`GetComponent`で共有化した完成例がこちらです。
https://gist.github.com/JunShimura/866cfe8736e4b40f35bf50113748cdd0

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace CommonTools
{
    public class Tools : MonoBehaviour
    {
        public static T GetComponentInObject<T>(string objectName)
        {
            T c = default(T);
            GameObject go = GameObject.Find(objectName);
            if (go == null) {
                LogError(objectName);
            }
            else {
                c = go.GetComponent<T>();
                if (c == null) {
                    LogError(objectName + "." + nameof(T));
                }
            }
            return c;
        }
        static private void LogError(string s)
        {
            // StackFrameクラスでstackを2階層戻る
            System.Diagnostics.StackFrame objStackFrame = new System.Diagnostics.StackFrame(2);
            string methodName = objStackFrame.GetMethod().Name;
            Debug.LogError(s + " is not found at "
                + objStackFrame.GetMethod().ReflectedType.FullName
                + "." + objStackFrame.GetMethod().Name);
        }

    }

}
```
こうすると、

```c#
class GameDirector{
    void Start()
    {
        // hpゲージを取得
        hpGaugeImage = Tools.GetComponentInObject<Image>("hogeGauge");
```

このようなエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72979/d8efae51-c203-2ff7-e980-3ff88c8bdab3.png)

~~うるさいゲームデザイナーの相手をしなくていい~~親切に何がおかしいか判った方が効率が好いですね！