---
# System prepended metadata

title: C# 避免在lamda內使用iteration variable

---

# C# 避免在lamda內使用iteration variable
我們在使用lambda或是匿名函式時，需要避免再迴圈內使用迭代的變數。而遇到不如預期的結果，比如**每次執行的值都一樣、觸發OutOfRangeException**等等之類的問題(端看實作的情境)

```csharp
for (int i = 0; i < length; i++)
// i 表示 迭代的變數
```

## 問題情境

在開發遊戲時，有時會在迴圈中使用lamda 或是匿名函式來達成一些功能

比如說以下情境是，我撰寫一個迴圈，每次迴圈會透過Corutine等待一秒鐘後，印出當前Index

```csharp
public class TestLambda : MonoBehaviour
{
    private void Start()
    {
        int length = 3;
        for (int i = 0; i < length; i++)
        {
            StartCoroutine(
								WaitOutput(() => { Debug.Log(i); }
            ));
        }
    }

    public IEnumerator WaitOutput(Action OnComplete)
    {
        yield return new WaitForSeconds(1.0f);
        OnComplete?.Invoke();
    }
}
```

### 實際預期應該是

```csharp
1
2
3
```

### 實際執行結果

```csharp
3
3
3
```

## 發生原因

經過參考資料後，經過反組譯來查看IL的程式碼

```csharp
private void Start()
{
	//IL_001b: Unknown result type (might be due to invalid IL or missing references)
	//IL_0025: Expected O, but got Unknown
	int num = 10;

	<>c__DisplayClass0_0 CS$<>8__locals0 = new <>c__DisplayClass0_0();
	CS$<>8__locals0.i = 0;
	while (CS$<>8__locals0.i < num)
	{
            ((MonoBehaviour)this).StartCoroutine(WaitOutput((Action)delegate
		{
			Debug.Log((object)CS$<>8__locals0.i);
			));
		CS$<>8__locals0.i++;
	}
}

[IteratorStateMachine(/*Could not decode attribute arguments.*/)]
public IEnumerator WaitOutput(Action OnComplete)
{
	yield return new WaitForSeconds(1f);
	if (OnComplete != null)
	{
			OnComplete.Invoke();
	}
}
```

觀察印出Index的程式碼

 `Debug.Log((object)CS$<>8__locals0.i);`

印出的Index則是儲存於`CS$<>8__locals0` 物件中的變數`i`

物件`CS$<>8__locals0` 則是起初宣告於迴圈之外

 `<>c__DisplayClass0_0 CS$<>8__locals0 = new <>c__DisplayClass0_0();`

每次執行完迴圈便會執行

 `CS$<>8__locals0.i++` 將index的值+1

故最終執行完迴圈後

所有的lamda內指向的物件`CS$<>8__locals0` 都是**同一個**

因此會得到相同的結果

## 改善的方式

我們在迴圈內將**新增一個變數儲存迭代變數的數值**

```csharp
for (int i = 0; i < length; i++)
// 將i的數值儲存於localVariable		
int localVariable = i;  
```
**改善後的程式碼**

```csharp
public class TestLambda : MonoBehaviour
{
    private void Start()
    {
        int length = 3;
        for (int i = 0; i < length; i++)
        {
    		int localVariable = i;
		StartCoroutine(
			WaitOutput(() => { Debug.Log(localVariable); }
		));
        }
    }

    public IEnumerator WaitOutput(Action OnComplete)
    {
        yield return new WaitForSeconds(1.0f);
        OnComplete?.Invoke();
    }
}
```

**執行結果**

```csharp
1
2
3
```

**反組譯來查看IL的程式碼**

```csharp
private void Start()
{
	//IL_001d: Unknown result type (might be due to invalid IL or missing references)
	//IL_0027: Expected O, but got Unknown
	int num = 10;
	for (int i = 0; i < num; i++)
	{
		<>c__DisplayClass0_0 CS$<>8__locals0 = new 				<>c__DisplayClass0_0();
		CS$<>8__locals0.local = i;
		((MonoBehaviour)this).StartCoroutine(WaitOutput((Action)delegate
		{
			Debug.Log((object)CS$<>8__locals0.local);
		}));
	}
}

[IteratorStateMachine(/*Could not decode attribute arguments.*/)]
public IEnumerator WaitOutput(Action OnComplete)
{
	yield return new WaitForSeconds(1f);
	if (OnComplete != null)
	{
		OnComplete.Invoke();
	}
}
```

原先的`CS$<>8__locals0` 被宣告於迴圈之內

故所有的lamda內指向的物件，皆為**不同的物件**

所以lamda 執行的結果，才會與預期結果相符

若有任何錯誤的內容，歡迎大家指正～

參考資料：

- **[Why is it bad to use an iteration variable in a lambda expression](https://stackoverflow.com/questions/227820/why-is-it-bad-to-use-an-iteration-variable-in-a-lambda-expression)**
- [**https://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach**](https://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach)
- [https://blog.csdn.net/p15097962069/article/details/105200833](https://blog.csdn.net/p15097962069/article/details/105200833)