ジェネリックメソッドの考え方

2020年10月13日

UnityでのGetComponent<>()の動作についてEngineをエミュレートで考えます。

まず、次のコードを見てください。

using UnityEngineEmulater;

namespace GenericTest
{
    class PlayerController
    {
        public void Start()
        {
            var gameObject = new GameObject();
            var rigidbody = gameObject.GetComponent<Rigidbody>();
            rigidbody.Addforce();
        }
    }
}

classから抜き出してみます。

class PlayerController
{
    public void Start()
    {
        var gameObject = new GameObject();
        var rigidbody = gameObject.GetComponent<Rigidbody>();
        rigidbody.Addforce();
    }
}

Unityでゲームを作っているよく見かける形ですね。
ここで、

GetComponent<>();

の部分を見てください。これは、どのようなコードなのでしょう。
呼び出されるメソッドは、次のようになります。

public T GetComponent<T>()
{
    // このコードは省略されたもので、実際のUnityEngineではありません。
    return new T();
}

Tとか<T>とか、?ですね。ジェネリックの書式は次のようになります。

[修飾子] 戻り値の型 メソッド名<型パラメータ,….>(引数の型 引数名, …)

Tは、実は何でも良いのですが、Typeの略なので慣例で頭文字を使います。
 ジェネリックメソッドとは、メソッドの引数や配下のローカル変数の型を、メソッドを呼び出す際に決められるメソッドのことなのです。

この書式を使うと色々な型を受け入れることができるメソッドを作ることができます。

<Collision>でメソッドを呼び出すこともできます。

では、エミュレートしたコードを見てみましょう。
上のPlayerControllerクラスと共にプロジェクトを作成し、実行すると、

AddForceを実行

と表示されます。

ComponentクラスのGetComponent<T>()メソッドがありますね。
これはエミュレートなので、PlayerControllerクラス用のStart()メソッドを呼び出す処理がMainメソッドに記述されています。

using System;

namespace UnityEngineEmulater
{
    class Program
    {
        static void Main(string[] args)
        {
            var playerController = new GenericTest.PlayerController();
            playerController.Start();
        }
    }

    class GameObject : Component { }

    class Component
    {
        public T GetComponent<T>() where T : new()
        {
            return new T();
        }
    }

    class Rigidbody
    {
        public void Addforce()
        {
            Console.WriteLine("AddForceを実行");
        }
    }
}

上記にColliderコンポーネント追加、型制約をつけた例です。

using System;

namespace UnityEngineEmulater
{
    class Program
    {
        static void Main(string[] args)
        {
            var playerController = new GenericTest.PlayerController();
            playerController.Start();
        }
    }

    class GameObject : Component { }

    class Component
    {
        public T GetComponent<T>() where T : IComponent, new()
        {
            return new T();
        }
    }

    interface IComponent { }

    class Rigidbody : IComponent
    {
        public void Addforce()
        {
            Console.WriteLine("AddForceを実行");
        }
    }

    class Collider : IComponent { }
}

intellisenseで整理します。

using System;

namespace UnityEngineEmulater
{
    class Program
    {
        static void Main() => new GenericTest.PlayerController().Start();
    }

    class GameObject : Component { }

    class Component
    {
        public T GetComponent<T>() where T : IComponent, new() => new T();
    }

    interface IComponent { }

    class Rigidbody : IComponent
    {
        public void Addforce() => Console.WriteLine("AddForceを実行");
    }

    class Collider : IComponent { }
}

C#,Unity

Posted by hidepon