C# - 람다식

김도현·2023년 10월 20일
0

TIL

목록 보기
53/76

람다 함수

람다 함수이란 마치 오브젝트를 탄생시키듯이 코드 도중에 이름이 없는 함수를 만들고 마치 변수에 값을 넣어 주듯이 여기저기에 줄 수 있다.

형식

(input-parameters) => {expression;};

델리게이트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Messenger : MonoBehaviour
{
    public delegate void Send(string reciver);

    Send onSend;

    void SendMail(string reciever)
    {
        Debug.Log("Mail sent to: " + reciever);
    }

    void SendMoney(string reciever)
    {
        Debug.Log("Money sent to: " + reciever);
    }

    void Start()
    {
        onSend += SendMail;
        onSend += SendMoney;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onSend("anso");
        }
    }
}

이렇게 작성하고 Unity에서 스페이스바를 누르면
Mail sent to: ansoMoney sent to: anso가 콘솔창에 실행된다.

하지만 이코드를 람다 함수로 바꾸면 어떻게 될까요?

람다 함수로 바꾸기

식 형식의 람다식 (Expression Lambda)

void SendMail(string reciever)
{
	Debug.Log("Mail sent to: " + reciever);
}
void SendMoney(string reciever)
{
	Debug.Log("Money sent to: " + reciever);
}

위의 코드를

onSend += (reciever) => Debug.Log("Mail sent to: " + reciever);
onSend += (reciever) => Debug.Log("Mail sent to: " + reciever);

와 같이 바꿀수 있습니다.

메서드의 이름은 없으며 즉석에서 구현되는 함수.
reciever은 매개 변수이며 입력값이 한개이면 Type을 안붙여줘도 알아서 처리합니다.

문 형식의 람다식 (Statement Lambda)

void SendMail(string reciever)
{
	Debug.Log("Mail sent to: " + reciever);
}
void SendMoney(string reciever)
{
	Debug.Log("Money sent to: " + reciever);
}

위의 코드를

onSend += (reciever) => 
	{
		Debug.Log("Mail sent to: " + reciever);
        Debug.Log("Money sent to: " + reciever);
	}
    //또는 위의 메서드를 그대로 사용해서
    {
    	SendMail(reciever);
    	SendMoney(reciever);
    }

이 처럼 바꿀수 있다.

Func 대리자 & Action 대리자

  • 익명 메소드/무명 함수 정의를 위해 매번 대리자를 선언해야하는 불편 제거
  • .NET 라이브러리에 미리 선언되어 있는 대리자
    • Func 대리자 : 반환 값이 있는 익명 메소드/무명 함수용 대리자
    • Action 대리자 : 반환 값이 없는 익명 메소드/무명 함수용 대리자

Func 대리자

  • 결과를 반환하는 메소드를 참조하기 위해 생성
    • 모든 Func 대리자의 형식 매개변수에서 마지막은 무조건 반환 형식
  • 17가지 버전
    • 입력 매개변수가 16개 이상이던지, ref나 out 한정자로 수식된 매개변수를 사용해야 하는 경우가 아니면 별도의 대리자를 만들어 쓸 필요가 없다.
using System;

namespace FuncTest
{
    class MainApp
    {
        static void Main(string[] args)
        {
            // Func<TResult> : 입력 매개변수 없는 버전
            Func<int> func1 = () => 10; // 입력 매개변수는 없으며, 무조건 10 반환
            Console.WriteLine($"func1() : {func1()}");

            // Func<T1, TResult> : 매개변수 1개
            Func<int, int> func2 = (x) => x * 2;  // 입력 매개변수는 int 형식 하나, 반환 형식도 int
            Console.WriteLine($"func2(4) : {func2(4)}"); 

            // Func<T1, T2, TResult> : 매개변수 2개
            Func<double, double, double> func3 = (x, y) => x / y; // 입력 매개변수는 double 형식 둘, 반환 형식도 double
            Console.WriteLine($"func3(22, 7) : {func3(22, 7)}");
        }
    }
}

Action 대리자

  • 반환 형식이 없고 입력 매개변수를 위해 선언
    • 결과 반환보다 일련의 작업을 수행하는 것이 목적
using System;

namespace ActionTest
{
    class MainApp
    {
        static void Main(string[] args)
        {
        	// Action<T> : 매개변수 1개 버전
            Action act1 = () => Console.WriteLine("Action()");            
            act1();

            int result = 0;
            Action<int> act2 = (x) => result = x * x;  // result에 x * x 결과 저장

            act2(3);
            Console.WriteLine($"result : {result}");
			
            
            // Action<T1, T2> : 매개변수 2개 버전
            Action<double, double> act3 = (x, y) =>
                {
                    double pi = x / y;
                    Console.WriteLine($"Action<T1, T2>({x}, {y}) : {pi}");
                };
            
            act3(22.0, 7.0); 
        }
    }
}

응용

Unity UIpopup을 작업할때 사용했던 코드 입니다.

//UIPopup.cs
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class UIPopup : MonoBehaviour
{
    [SerializeField] private TMP_Text txtTitle;
    [SerializeField] private TMP_Text txtContent;

    [SerializeField] private Button _btnBack;
    [SerializeField] private Button _btnConform;
    [SerializeField] private Button _btnCancel;
    [SerializeField] private GameObject Popup;

    private Action _onConfirm;

    void Start()
    {
        _btnBack.onClick.AddListener(Close);
        _btnCancel.onClick.AddListener(Close);
        _btnConform.onClick.AddListener(Confirm);
    }

    public void SetPopup(string title, string content, Action onConfirm)
    {
        txtTitle.text = title;
        txtContent.text = content;

        _onConfirm = onConfirm;
    }

    void Confirm()
    {
        _onConfirm?.Invoke();
        _onConfirm = null;
        Close();
    }

    void Close()
    {
        Popup.SetActive(false);
    }
}

// 다른 클래스 안
private UIPopup _uiPopup;
[SerializeField] private GameObject _SetName;
[SerializeField] private GameObject _Popup;
[SerializeField] private Button _btnNameConfirm;
private SetPlayerStatus _SetPlayerStatus;

private void Start()
{
    _uiPopup = GetComponent<UIPopup>();
	_SetPlayerStatus = GetComponent<SetPlayerStatus>();
	_btnNameConfirm.onClick.AddListener(OpenPopup_SetPlayerName);
} 

void OpenPopup_SetPlayerName()
{
    _Popup.SetActive(true);
    _uiPopup.SetPopup("확인", "정말 결정하시겠습니까?", SetPlayerName_Event);
}

void SetPlayerName_Event()
{
    _SetPlayerStatus.SetPlayerName();
    _SetName.SetActive(false);
    _SetGmaeDifficulty.SetActive(true);
}

식 트리(Expression Tree)

식 트리의 내용 너무 길고 복잡하기에 다음번에 이어서 작성하도록 하겠습니다.

0개의 댓글