Unity & C#

[C#] for loop에서 Lambda 사용 시 iteration variable 사용 주의

왼손잡이개발자 2021. 12. 11. 11:40

 

C#이나 Javascript 두 언어 다 개인적으로 Lambda 식을 많이 사용하는데요. 최근 for loop 안에서 lambda 식을 사용 했을 때 예상치 못한 결과가 계속 나와서 충격을 받았던 경험이 있었습니다. 그 경험을 공유하려고 합니다.

 

예시 상황은, Button들이 array에 담겨져 있고 for loop를 돌면서 이벤트를 등록했습니다.

using UnityEngine;
using UnityEngine.UI;

namespace LambdaInForLoop
{
	public class ButtonManager : MonoBehaviour
	{
		[SerializeField] private Button[] buttons;

		void Start()
		{
			for (int i = 0; i < buttons.Length; i++)
			{
				buttons[i].onClick.AddListener(() =>
				{
					print(i);
				});
			}
		}
	}

}

이렇게 했을 때 각 버튼을 눌렀을 때 예상 결과는 각 순서에 맞는 숫자가 print 되어야 할텐데요. (예를 들어서 첫번째 버튼은 0, 두번째는 1, 세번째는 2,... ). 그런데 충격적인 결과가 나왔습니다. 모든 버튼이 같은 수를 계속 print 했습니다. 그리고 그 수는 buttons.Length 와 같았지요. 그래서 등록된 버튼이 3개였으면 모든 버튼이 3을 print 했습니다. 

 

인터넷에 검색해 보니 for loop의 iteration variable을 lambda 식에 그대로 사용 했을 시 value type이 아닌 reference 타입 처럼 값을 capture 해두고 있다가 사용된다고 하더라구요. 그래서 for loop에서 제일 마지막 loop에서 i++되었을 때 값이 capture 되어있다가 버튼이 클릭 되었을 때 buttons.Length 값이 print 되었습니다.

 

그렇다면 이걸 어떻게 해결할 수 있을까요? 간단합니다. for loop안에 local variable을 하나 두고 복사 해서 사용하면 된다고 합니다. 

using UnityEngine;
using UnityEngine.UI;

namespace LambdaInForLoop
{
	public class ButtonManager : MonoBehaviour
	{
		[SerializeField] private Button[] buttons;

		void Start()
		{
			for (int i = 0; i < buttons.Length; i++)
			{
				buttons[i].onClick.AddListener(() =>
				{
					int copy = i;
					print(copy);
				});
			}
		}
	}

}

위 코드에서 int copy = i; 부분이 바로 복사를 하는 부분입니다.

 

혹시 Lambda 식을 자주 사용하시는 분들은 꼭 알아두셔야 할 내용인 것 같네요~