📗

【Unity】型の拡張メソッドについて

2024/09/27に公開

はじめに

初めての方も、そうでない方もこんにちは!
現役ゲームプログラマーのたむぼーです。
自己紹介を載せているので、気になる方は見ていただければ嬉しいです!

今回は
 型の拡張メソッド
について紹介します

https://zenn.dev/tmb/articles/1072f8ea010299

拡張メソッドとは

第一引数にthis 対象の型 変数名を入れた関数を作ることで、以下のように呼び出せる関数です

static public class StringEx
{
    /// <summary>
    /// ログを表示
    /// </summary>
    static public void Log(this string message)
    {
        Debug.LogError("Log : " + message);
    }
}

public class Example
{
    private string _message;

    public void Example1()
    {
        _message = "これはExample1です!";
        _message.Log(); // StringExのLogを呼び出す
    }
}
実行結果
Log : これはExample1です!

いろいろな型の拡張メソッド

GameObjectEx

GameObjectEx.cs
using UnityEngine;

namespace ProjectName.Ex
{
    static public class GameObjectEx
    {
        /// <summary>
        /// コンポーネント取得/追加
        /// </summary>
        static public T GetOrAddComponent<T>(this GameObject self) where T : Component
        {
            if (self.TryGetComponent<T>(out T component))
            {
                return component;
            }
            return self.AddComponent<T>();
        }

        /// <summary>
        /// アクティブ状態の設定
        /// </summary>
        static public void SetActiveSafe(this GameObject self, bool isActive)
        {
            if (self == null)
            {
                return;
            }
            if (self.activeSelf != isActive)
            {
                self.SetActive(isActive);
            }
        }
    }
}

ComponentEx

ComponentEx.cs
using UnityEngine;

namespace ProjectName.Ex
{
    static public class ComponentEx
    {
        /// <summary>
        /// コンポーネント取得/追加
        /// </summary>
        static public T GetOrAddComponent<T>(this Component self) where T : Component
        {
            if (self.gameObject.TryGetComponent<T>(out T component))
            {
                return component;
            }

            return self.gameObject.AddComponent<T>();
        }

        /// <summary>
        /// アクティブ状態の設定
        /// </summary>
        static public void SetActiveSafe(this Component self, bool isActive)
        {
            if (self == null)
            {
                return;
            }
            self.gameObject.SetActiveSafe(isActive);
        }

        /// <summary>
        /// ルートのキャンバスを取得
        /// </summary>
        static public Canvas FindRootCanvas(this Component self)
        {
            return self.transform.root.GetComponentInChildren<Canvas>();
        }
    }
}

DictionaryEx

DictionaryEx.cs
using System.Collections.Generic;

namespace ProjectName.Ex
{
    static public class DictionaryEx
    {
        /// <summary>
        /// 安全な獲得
        /// </summary>
        static public V GetValueSafe<K, V>(this Dictionary<K, V> self, K key)
        {
            if (self.TryGetValue(key, out V value))
            {
                return value;
            }
            return default;
        }

        /// <summary>
        /// 安全な追加
        /// </summary>
        static public void AddValueSafe<K, V>(this Dictionary<K, V> self, K key, V value)
        {
            if (self.ContainsKey(key))
            {
                self[key] = value;
            }
            else
            {
                self.Add(key, value);
            }
        }
    }
}

ListEx

ListEx.cs
using System.Collections.Generic;

namespace ProjectName.Ex
{
    static public class ListEx
    {
        /// <summary>
        /// 安全な追加
        /// </summary>
        static public void AddSafe<T>(this List<T> self, T data) where T : class
        {
            if (data == null)
            {
                return;
            }
            self?.Add(data);
        }

        /// <summary>
        /// 安全な獲得
        /// </summary>
        static public V GetValueSafe<V>(this List<V> self, int id)
        {
            if (self == null || id < 0 || self.Count <= id)
            {
                return default(V);
            }
            return self[id];
        }
    }
}

ObjectEx

ObjectEx.cs
using UnityEngine;

namespace ProjectName.Ex
{
    static public class ObjectEx
    {
        /// <summary>
        /// 安全にキャスト
        /// </summary>
        static public bool SafeCast<T>(this object obj, out T result)
        {
            if (obj is T castedValue)
            {
                result = castedValue;
                return true;
            }
            else
            {
                Debug.LogError($"Failed to cast {obj} to {typeof(T)}");
                result = default;
                return false;
            }
        }
    }
}

StringEx

StringEx.cs
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System;

namespace ProjectName.Ex
{
    static public class StringEx
    {
        /// <summary>
        /// 指定されたフォーマットで文字列を置換
        /// </summary>
        static public string ReplaceFormat(this string str, Dictionary<string, string> param)
        {
            StringBuilder builder = UseBuilder();
            builder.Append(str);

            foreach (var val in param)
            {
                builder.Replace(val.Key, val.Value);
            }
            return Flash(builder);
        }

        /// <summary>
        /// 指定されたフォーマットで文字列を追加
        /// </summary>
        static public string AppendFormat(this string format, string str)
        {
            StringBuilder builder = UseBuilder();
            if (string.IsNullOrEmpty(format))
            {
                return str;
            }
            builder.AppendFormat(format, str);
            return Flash(builder);
        }

        /// <summary>
        /// 指定されたフォーマットで2つの文字列を追加
        /// </summary>
        static public string AppendFormat(this string format, string str, string str2)
        {
            StringBuilder builder = UseBuilder();
            builder.AppendFormat(format, str, str2);
            return Flash(builder);
        }

        /// <summary>
        /// 指定されたフォーマットで整数を追加
        /// </summary>
        static public string AppendFormat(this string format, int str)
        {
            StringBuilder builder = UseBuilder();
            builder.AppendFormat(format, str);
            return Flash(builder);
        }

        /// <summary>
        /// 指定されたフォーマットで2つの整数を追加
        /// </summary>
        static public string AppendFormat(this string format, int str, int str2)
        {
            StringBuilder builder = UseBuilder();
            builder.AppendFormat(format, str, str2);
            return Flash(builder);
        }

        /// <summary>
        /// 指定されたフォーマットで可変長引数を追加
        /// </summary>
        static public string AppendFormat(this string format, params object[] p)
        {
            StringBuilder builder = UseBuilder();
            builder.AppendFormat(format, p);
            return Flash(builder);
        }

        /// <summary>
        /// 文字列を結合
        /// </summary>
        static public string Join(this string self, string join)
        {
            StringBuilder builder = UseBuilder();
            builder.Append(self);
            builder.Append(join);
            return Flash(builder);
        }

        /// <summary>
        /// パス文字列を結合
        /// </summary>
        static public string JoinPath(this string self, string join)
        {
            self = self.Replace("\\", "/").Trim();

            StringBuilder builder = UseBuilder();

            builder.Append(self);
            if (!self.EndsWith("/"))
            {
                builder.Append("/");
            }
            builder.Append(join);

            return Flash(builder);
        }

#if UNITY_EDITOR
        /// <summary>
        /// Assetsフォルダ内のパスを作成
        /// </summary>
        static public string MakeAssetsPath(this string self)
        {
            return UnityEngine.Application.dataPath.JoinPath(self.Replace("\\", "/").Replace("Assets", ""));
        }
#endif

        /// <summary>
        /// 文字列リストを結合
        /// </summary>
        static public string Combine(this List<string> self)
        {
            return Combine(self, Environment.NewLine);
        }

        /// <summary>
        /// 指定された区切り文字で文字列リストを結合
        /// </summary>
        static public string Combine(this List<string> self, string separator)
        {
            if (self == null || self.Count <= 0)
            {
                return "";
            }
            StringBuilder builder = UseBuilder();
            for (int i = 0; i < self.Count; i++)
            {
                if (0 < i)
                    builder.Append(separator);

                builder.Append(self[i]);
            }
            return builder.ToString();
        }


        /// <summary>
        /// 改行の設定
        /// </summary>
        static public string ReplaceNewLine(this string str)
        {
            return ReplaceNewLine(str, Environment.NewLine);
        }

        /// <summary>
        /// 改行の削除
        /// </summary>
        static public string OmitNewLine(this string str)
        {
            return ReplaceNewLine(str, "");
        }

        /// <summary>
        /// 改行の設定
        /// </summary>
        static public string ReplaceNewLine(this string str, string ch)
        {
            var builder = UseBuilder();
            builder.Append(str);
            builder.Replace("\n\r", "[NR]");
            builder.Replace("\r\n", "[NR]");
            builder.Replace("\r", "[NR]");
            builder.Replace("\n", "[NR]");
            builder.Replace("[NR]", ch);
            return Flash(builder);
        }

        /// <summary>
        /// 数値のFormat指定での文字列化
        /// </summary>
        static public string ToStringFormat(this int val, string format)
        {
            var builder = UseBuilder();
            builder.AppendFormat(format, val);
            return Flash(builder);
        }

        /// <summary>
        /// 数値のFormat指定での文字列化
        /// </summary>
        static public string ToStringFormat(this float val, string format)
        {
            var builder = UseBuilder();
            builder.AppendFormat(format, val);
            return Flash(builder);
        }


        /// <summary>
        /// Int化
        /// </summary>
        static public int ToInt(this string self)
        {
            if (int.TryParse(self, out int result))
            {
                return result;
            }
            return 0;
        }

        /// <summary>
        /// Long化
        /// </summary>
        static public long ToLong(this string self)
        {
            if (long.TryParse(self, out long result))
            {
                return result;
            }
            return 0;
        }

        /// <summary>
        /// Double化
        /// </summary>
        static public double ToDouble(this string self)
        {
            if (double.TryParse(self, out double result))
            {
                return result;
            }
            return 0d;
        }

        /// <summary>
        /// Float化
        /// </summary>
        static public float ToFloat(this string self)
        {
            if (float.TryParse(self, out float result))
            {
                return result;
            }
            return 0.0f;
        }

        /// <summary>
        /// Bool化
        /// </summary>
        static public bool ToBool(this string self)
        {
            if (bool.TryParse(self, out bool result))
            {
                return result;
            }
            return false;
        }

        /// <summary>
        /// Enum化
        /// </summary>
        static public T ToEnum<T>(this string self) where T : struct
        {
            if (Enum.TryParse(self, out T result) && Enum.IsDefined(typeof(T), result))
            {
                return result;
            }
            throw new ArgumentException($"Invalid Enum value for type {typeof(T)}: {self}");
        }

        /// <summary>
        /// Enum化
        /// </summary>
        static public object ToEnum(this string self, Type type)
        {
            if (Enum.IsDefined(type, self))
            {
                return Enum.Parse(type, self);
            }
            throw new ArgumentException($"Invalid Enum value for type {type}: {self}");
        }


        /// <summary>
        /// 行数カウンター
        /// </summary>
        static public int LineCount(this string self)
        {
            return self.Count(c => c == '\n') + 1;
        }

        /// <summary>
        /// 文字列の末尾が一致するか
        /// </summary>
        static public bool EndsWithEx(this string a, string b)
        {
            if (a == null || b == null || a.Length < b.Length)
            {
                return false;
            }
            return a.Substring(a.Length - b.Length) == b;
        }

        private static List<StringBuilder> _builderPool = new List<StringBuilder>();
        private const int MaxPoolSize = 10;

        /// <summary>
        /// ビルダーの取得
        /// </summary>
        static public StringBuilder UseBuilder()
        {
            StringBuilder builder = null;
            lock (_builderPool)
            {
                if (_builderPool.Count > 0)
                {
                    builder = _builderPool[_builderPool.Count - 1];
                    _builderPool.RemoveAt(_builderPool.Count - 1);
                }
            }

            return builder ?? new StringBuilder();
        }

        /// <summary>
        /// ビルダーの返却と文字列の取り出し
        /// </summary>
        static public string Flash(StringBuilder builder)
        {
            var str = builder.ToString();
            lock (_builderPool)
            {
                if (_builderPool.Count < MaxPoolSize)
                {
                    _builderPool.Add(builder);
                }
                builder.Length = 0;
            }
            return str;
        }
    }
}

Discussion