# C# Yield

#CSharp #yield

# yield return
## 理解及应用
yield就是生成了一个值之后，控制回到了调用方继续执行，执行完成后，需要下一个值的时候，会重新回到yield return之后的语句处，继续执行。

结合下面的程序，理解了这点之后，跟yield相关的知识基本就都了解了。

代码：
```csharp
public static void Main()
{
    var testClass = new TestClass();
    Console.WriteLine("[Main] BeginTest");
    foreach (var value in testClass)
    {
	    Console.WriteLine($"[Main] Got Value {value}");  
        Console.WriteLine("[Main] Do some logic here");  
        Console.WriteLine("[Main] Try to get next value");  
    }
}

public class TestClass : IEnumerable<int>  
{  
    public IEnumerator<int> GetEnumerator()  
    {        
	    Console.WriteLine("[Class] Begin Generate");  
        for (var index = 0; index < 3; index++)  
        {   
	        Console.WriteLine($"[Class] Generate {index}");  
            yield return index;  
            Console.WriteLine($"[Class] Finished send {index}");  
        }  
        Console.WriteLine("[Class] Finished Generate");  
    }  
    
    IEnumerator IEnumerable.GetEnumerator()  
    {   
	    return GetEnumerator();  
    }
}
```

程序执行日志：
```log
[Main] BeginTest
[Class] Begin Generate
[Class] Generate 0
[Main] Got Value 0
[Main] Do some logic here
[Main] Try to get next value
[Class] Finished send 0
[Class] Generate 1
[Main] Got Value 1
[Main] Do some logic here
[Main] Try to get next value
[Class] Finished send 1
[Class] Generate 2
[Main] Got Value 2
[Main] Do some logic here
[Main] Try to get next value
[Class] Finished send 2
[Class] Finished Generate
```

## 递归迭代器
代码：
```csharp
public static void Main()  
{  
    var testClass = new TestClass();  
    testClass.Value = 1;  
    testClass.SubClasses = new List<TestClass>  
    {   
	    new()  
        {   
	        Value = 2,  
            SubClasses = new List<TestClass>  
            {   
	            new() { Value = 3 },  
                new() { Value = 4 }  
            }
        },
        new() { Value = 5 }  
    };  
    foreach (var item in testClass)  
    {
	    Console.WriteLine(item);  
    }
}

public class TestClass : IEnumerable<int>  
{  
    public IList<TestClass> SubClasses { get; set; }  
    public int Value { get; set; }  
    public IEnumerator<int> GetEnumerator()  
    {        
	    yield return Value;  
	    
        if (SubClasses is not { Count: > 0 }) { yield break; }  
        
        foreach (var subClass in SubClasses)  
        {            
	        foreach (var item in subClass)  
            {    
	            yield return item;  
            }
        }
    }  
    
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();  
}
```

程序执行日志：
```log
1
2
3
4
5
```

## 注意
递归迭代器存在一定的风险，如果是不平衡二叉树的话，可能会很深，获取值的时候的递归迭代的成本较高。

## 原理
C# 编译器在遇到迭代器的时候，会根据枚举数模式（[Iterator Pattern](https://codewithshadman.com/iterator-pattern-csharp/#what-is-iterator-pattern)）将代码展开成CLI。

在生成的代码中，C# 编译器会首先创建一个嵌套的私有类来实现 `IEnumerator<T>` 接口，以及它的 `Current` 属性和 `MoveNext()` 方法。

`Current` 属性返回与迭代器的返回类型对应的一个类型。

C# 编译器检查包含在迭代器中的代码，并在 `MoveNext()` 方法和 `Current` 属性中创建必要的代码来模拟它的行为。

# yield break
取消更多的迭代

# 注意
1. `yield` 语句只能在方法、用户自定义操作符、或者索引器或属性的 `get` 防蚊器方法中出现。成员不可以获取任何 `ref` 或 `out` 参数
2. `yield` 语句不能在匿名语句或者 `lambda` 方法中出现
3. `yield` 语句不能在 `try` 语句的 `catch` 和 `final` 子句中出现。除此之外，`yield` 语句能在 `try` 块中出现的前提是，它没有 `catch` 块
