C#

[C#][Unity] Extention (확장 메서드)

자가라o 2021. 8. 1. 15:55

<Extention> (확장 메서드)

  1. 확장 메서드를 사용하면 기존 형식에 메서드를 "추가"할 수 있다.
    • 주로 readonly나 상속 불가능한 클래스에 필요한 기능을 추가할 때 사용한다.
  2. 확장 메서드와 일반 메서드를 호출하는 데는 명백한 차이가 없다고 한다.
    • 확장 메서드는 정적 메서드이지만 확장 형식의 인스턴스 메서드인 것처럼 호출된다.

놀랍게도 가장 일반적인 확장 메서드는 (쿼리 기능을 기존 System.Collections.IEnumerableSystem.Collections.Generic.IEnumerable<T> 형식에 추가하는) LINQ 표준 쿼리 연산자라고 한다!


<사용>

  1. static 클래스를 만든 후 확장메서드로 사용할 static 메서드를 만든다.
  2. 확장하려는 클래스를 매개변수의 첫번째로 두고 앞에 키워드 this를 붙인다.
  3. 확장하려는 클래스의 인스턴스 뒤에 dot을 찍고 확장 메서드를 사용한다.

<예>

Sealed Class

유니티의 GameObject.

namespace UnityEngine
{
    public sealed class GameObject : Object
    {
        public GameObject();        
        public GameObject(string name);        
        public GameObject(string name, params Type[] components);
        
        ...
    }
}

GameObject의 경우 sealed class이기 때문에 사용자가 임의로 메소드를 추가하기 어렵다.

public class SomeClass
{
    // gameObject의 위치를 변화시킨 뒤
    // 이름과 위치를 로그로 표시하는 메서드
    public void Method_A(GameObject gameObject, Vector3 vec)
    {
        gameObject.transform.position += vec;
        Debug.Log($"이름 : {gameObject.name}, 위치 : {gameObject.transform.position}");
    }

    // 사용시
    public void Use_Method_A()
    {
        GameObject gameObject = new GameObject();
        Mothod_A(gameObject, Vector3.forward);
    }
}

일반적인 상황에서는 SomeClass 내부에 GameObject에 원하는 명령을 실행하는 함수를 따로 만들어 사용하지만

public static class Extension
{
    public static void Method_B(this GameObject gameObject, Vector3 vec)
    {
        gameObject.transform.position += vec;
        Debug.Log($"이름 : {gameObject.name}, 위치 : {gameObject.transform.position}");
    }

}

public class SomeClass
{
    // gameObject의 위치를 변화시킨 뒤
    // 이름과 위치를 로그로 표시하는 메서드
    /*public void Method_A(GameObject gameObject, Vector3 vec)
    {
        gameObject.transform.position += vec;
        Debug.Log($"이름 : {gameObject.name}, 위치 : {gameObject.transform.position}");
    }*/

    // 사용시
    public void Use_Method_B()
    {
        // 1번
        GameObject gameObject = new GameObject();        
        gameObject.Mothod_A(Vector3.forward);  
        
        // 2번
        GameObject gameObject = new GameObject().Mothod_A(Vector3.forward); 
    }
}

확장 메서드를 사용하면 마치 Method_B()가 GameObject의 메서드인 것처럼 쓸 수 있다.


Readonly Property

Transform.position. x,y

public class SomeClass
{    
    // 일반적으로 변경할 수 없는 Transform.position.x,y
    public void Change_X()
    {
        transform.position = new Vector3(3, tr.position.y); 
    }
}
public static class Extension
{
    public static void x(this Transform tr, float change)
    {
        tr.position = new Vector3(change, tr.position.y);
    }
    public static void y(this Transform tr, float change)
    {
        tr.position = new Vector3(tr.position.x, change);
    }
}

public class SomeClass
{
    // 확장메서드에서 추가한 메서드로 
    // Transform의 메서드처럼 간편하게 사용할 수도 있다.
    public void Change_eachPos()
    {
    	transform.x(3);
        transform.y(-2);
    }
}

<그 외>

 

확장 메서드는 이름과 시그니처가 클래스에서 이미 구현된 일반메서드와 정확하게 일치할 경우 호출되지 않는다.

①일치하는 시그니처를 가진 인스턴스 메서드를 찾을 수 없으면 => ②컴파일러는 일치하는 확장명 메서드(있는 경우)에 바인딩한다.

 

확장메서드는 클래스의 수정이 불가능해 코드가 불필요하게 반복될때 매우 유용한 기능인 것 같다.