Skip to main content

Command Palette

Search for a command to run...

C# Yield

Published
2 min read
C# Yield

#CSharp #yield

yield return

理解及应用

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

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

代码:

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();  
    }
}

程序执行日志:

[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

递归迭代器

代码:

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();  
}

程序执行日志:

1
2
3
4
5

注意

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

原理

C# 编译器在遇到迭代器的时候,会根据枚举数模式(Iterator Pattern)将代码展开成CLI。

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

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

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

yield break

取消更多的迭代

注意

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

More from this blog

你愿意相信机器,还是人

春节回家,跟老爸一起刷了小破球2。 之后忽然想到了这个话题:你愿意相信机器,还是人 (一) 春节的时候,陪老爸去配了一副眼镜。 因为前不久我也正好花重金配了一副,在之前做了很多的功课,诸如眼镜的前倾角、面弯、镜间距、单眼瞳高、单眼瞳距之类的知识,了解了一些配镜过程中应该注意的事项。 老家是一个小县城,虽然眼镜店遍地都是,但专业的还真没几个。在配镜的过程中,能注意到这些细节的眼镜店几乎没有。蔡司专业的三维定位的仪器,更是全县城都没有。 当我提出需要仪器辅助测量这些参数的时候,一个配镜师傅跟我说,...

Jan 30, 20231 min read2
你愿意相信机器,还是人

Cache 缓存三问

哎,我一个个人项目基本上都没有超过过两位数的QPS的人,面试你天天问我缓存的这些问题,你们礼貌吗? 缓存穿透 是啥 当请求试图访问一个在缓存和数据库都不存在的 key时,因为缓存没有,所以请求会直接转发到数据库上。然而数据库里面也没有这个 key 的记录,所以也就没有办法将这个数据写入缓存。当下一次同样的请求来了的时候,还是会转发到数据库上。 在这种情况下,缓存基本上也就没用了,就像被穿透了一样,请求每次都会走到数据库,流量大时数据库可能会扛不住。 咋整 对请求进行校验。一些明显不合理的参数直...

Jun 17, 20221 min read18
Cache 缓存三问

C# 托管代码和垃圾回收

什么是托管代码 托管代码就是执行过程交由运行时管理的代码。运行时一般是指 CLR,公共语言运行时。 CLR 负责提取托管代码、将其编译成机器代码,然后执行它。除此之外,运行时还负责自动内存管理、安全边界、类型安全等等。 如果在 .Net 里面直接调用 C/C++ 程序,此类代码也称为“非托管代码”。在非托管代码的环境中,操作系统将程序加载进内存,然后调用内部的二进制代码,所以从内存管理到安全等诸多因素都需要程序员自己处理。 正常使用 .Net 编写的代码,会先编译成中间语言 (IL)。执行的...

Jun 14, 20221 min read43
C# 托管代码和垃圾回收

C# 发布-订阅模式

#CSharp #PubSub 发布-订阅模式 有些时候,发布-订阅模式也会被称为观察者模式,但其实,如果细分的话,这两者之间还是有些细微的差别的。 观察者模式是在主题者(Subject)内部,维护一个观察者(Observer)列表。然后当主题者的状态发生变更的时候,主题者可以通知观察者发生了变化。观察者可以主动访问主题者,获得变更。也可以在通知观察者的时候,直接带上变更的消息。 在观察者模式中,主题者和观察者还是存在部分的耦合。因此,我们可以引入一个中介,来接触这种耦合。 类 A(发布者)发生...

Jun 8, 20222 min read80
C# 发布-订阅模式

NightBack

7 posts