`
RednaxelaFX
  • 浏览: 3027700 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

C#中的generator不会被立即执行……

    博客分类:
  • C#
阅读更多
读了Eric Lippert的这帖:High maintenance之后,心里一阵寒——我自己写的代码里就有几乎一模一样的逻辑。

Eric的帖里举的不好的例子:
public static class StreamReaderExtensions
{
    public static IEnumerable<string> Lines(this StreamReader reader)
    {
        if (reader== null)
            throw new ArgumentNullException("reader");
        reader.BaseStream.Seek(0, SeekOrigin.Begin);
        string line;
        while ((line = reader.ReadLine()) != null)
            yield return line;
    }
}


我的代码:
/// <summary>
/// Reads the stream as lines of strings from the current
/// position to the end of the stream.
/// </summary>
/// <param name="reader">the stream to read from</param>
/// <returns>An enumerator of the lines of strings.</returns>
public static IEnumerable<string> Lines( this StreamReader reader ) {
    for ( var line = reader.ReadLine( ); null != line; line = reader.ReadLine( ) ) {
        yield return line;
    }
}

事实上我的还更糟糕一些,没做null检查。写扩展方法的时候我总是写着写着就忘了:扩展方法不是成员方法,作为“this”的参数需要做null检查。

Eric对他所举的例子指出了几个不足:
1、null检查不是在方法调用时执行,而是在迭代器第一次移动的时候才执行的
2、while循环在一行塞了太多逻辑,可以分开来写;
3、通过BaseStream来改变了底下的流的位置,不符合习惯上对流的使用方式;
4、调用者需要知道太多细节,例如必须知道何时这个迭代器结束了,在结束后由调用者来关闭流;
……等。

第4点我在写我那个程序时也知道这里有问题,但没想出好的解决办法。假如我就是要特意把流设置到某个位置之后再调用Lines(),该怎么办呢?或者说假如原本就不存在一个实际文件(例如stdin),那就无法用文件名来指定参数了,又怎么办呢?
于是在Stream的ownership上我还是得再思考一下才行。这篇主要是要提醒自己C#的generator的一个重要特性:

带有yield关键字的方法(也就是generator),其方法体在generator被调用时是不会被执行的;只有当其返回的iterator的MoveNext()方法被初次调用时才会执行。

举例来说,这样:
using System;
using System.Collections.Generic;

static class Program {
    static IEnumerable<int> FooGenerator( int max ) {
        Console.WriteLine( "Starting enumerator" );
        for ( var i = 0; i < max; ++i ) {
            yield return i;
        }
    }

    static void Main( string[ ] args ) {
        var list = FooGenerator( 3 );
        Console.WriteLine( "After Foo(), before foreach" );
        foreach ( var i in list ) {
            Console.WriteLine( i );
        }
    }
}

输出的结果会是:
After Foo(), before foreach
Starting enumerator
0
1
2


了解C#的generator的实现方式就不难理解这个行为的来源。C#的generator实际上是个由编译器自动生成的实现了IEnumerable<T>接口的有限状态机。也就是说generator里实际上只有一个return new ...,或许还会有些参数赋值,却没有任何别的内容。源码里写在generator里的逻辑都生成到了那个有限状态机对象里,也就是外界看到的迭代器里。如果要对参数做检查,恐怕还就是Eric说的,提供一个公有方法作为接口,在里面检查参数的正确性,然后再调用一个私有的generator来完成实际迭代工作。

用FooGenerator的例子说,假如max小于0是不符合要求的,那么应该这样写:
public static IEnumerable<int> FooGenerator( int max ) {
    if ( 0 > max ) {
        throw new ArgumentException( "Number must be non-negative", "max" );
    }
    return FooGeneratorCore( max );
}

private static IEnumerable<int> FooGenerator( int max ) {
    for ( var i = 0; i < max; ++i ) {
        yield return i;
    }
}
分享到:
评论

相关推荐

    [转帖] 用C# Generator解决Hanoi塔问题

    在C#中,可以使用`yield return`关键字来实现Generator的返回。 以下是一个简化的C#代码示例,展示了如何使用Generator来解决Hanoi塔问题: ```csharp public static IEnumerable&lt;string&gt; Hanoi(int n, char from,...

    CodeGenerator:C#代码生成器,用于从数据库读取表结构并生成相应的代码

    【描述】:CodeGenerator是一款C#编写的实用工具,它的主要功能是从数据库中提取表结构信息,并根据这些信息自动生成对应的代码,极大地提高了开发效率,减少了手动编写重复代码的工作量。 【详细知识点】: 1. **...

    mybatis中的generator工具

    在本篇文章中,我们将深入探讨MyBatis Generator的使用方法、主要功能以及如何结合MyBatis框架来提升开发效率。 一、MyBatis Generator简介 MyBatis Generator是一个基于Java的代码生成器,它能够根据数据库表结构...

    Node.js-在Express中愉快地使用Generator

    - Generator中的错误会被自动抛出,可以在外层捕获。 - 示例: ```javascript app.get('/', function*(req, res) { try { var result = yield someAsyncTask(); res.send(result); } catch (err) { res....

    hibernate中的generator的生成方式hibernate中的generator的生成方式

    hibernate中的generator的生成方式 hibernate中的generator的生成方式

    mybatis-generator-core-1.3.7-API文档-中文版.zip

    赠送jar包:mybatis-generator-core-1.3.7.jar; 赠送原API文档:mybatis-generator-core-1.3.7-javadoc.jar; 赠送源代码:mybatis-generator-core-1.3.7-sources.jar; 赠送Maven依赖信息文件:mybatis-generator-...

    MyBatis-Generator 离线安装

    总的来说,离线安装MyBatis-Generator涉及到下载jar文件、创建配置文件和数据库连接属性文件,以及在命令行中执行生成命令。理解这些步骤并根据项目需求进行适当调整,能帮助开发者高效地利用MyBatis-Generator进行...

    generator-ajax.zip

    在这个例子中,`fetch`操作被`yield`暂停,外部可以通过`next`方法传递结果给Generator,使得Generator能够继续执行。 接下来,我们讨论js进程中断。在JavaScript中,由于其单线程特性,一旦开始执行,无法直接中断...

    mybatis-generator生成数据库中文注释

    首先,你需要在项目中引入MyBatis Generator的依赖。如果是Maven项目,可以在pom.xml中添加以下依赖: ```xml &lt;groupId&gt;org.mybatis.generator &lt;artifactId&gt;mybatis-generator-core &lt;version&gt;1.4.2 ``` 接...

    C# ffmpeg 处理视频、C# ffmpeg播放视频

    FFmpeg库被许多编程语言所支持,包括C#。本篇将深入探讨如何在C#环境下利用FFmpeg进行视频处理以及播放视频。 首先,`C# ffmpeg.autogen`是指将FFmpeg的C语言接口自动生成为C#可用的接口,以便于在C#项目中调用...

    form-generator项目集成方案.doc

    在引入form-generator项目时,需要复制form-generator项目的components文件夹下的全部文件、uits文件夹下的所有文件以及views文件夹下全部文件到自己项目中,styles文件夹下全部文件复制到自己工程,icons文件夹下...

    mybatis-generator 解决中文注释乱码

    如果是eclipse工具,直接生成的,则替换:plugins/org.mybatis.generator.core_1.3.2*****.jar,如果是其他方式则直接替换掉官方的mybatis-generator-core1.3.2.jar即可

    EF 4.x POCO Entity Generator for C#

    做个记号。自己收集使用。 你也可通过以下链接下载 https://visualstudiogallery.msdn.microsoft.com/23df0450-5677-4926-96cc-173d02752313/

    mybatis逆向工具generator,中文注释,Byte改Integer

    MyBatis Generator是一款强大的逆向工程工具,它能够根据数据库中的表自动生成对应的Java实体类、Mapper接口和XML配置文件,极大地提高了开发效率。在给定的标题中,“mybatis逆向工具generator,中文注释,Byte改...

    mybatis generator

    Mybatis Generator的核心功能在于通过配置文件指定数据库连接信息、表名以及生成规则,然后通过执行Java代码,即可自动生成对应的Mapper接口、XML映射文件和实体类。这样不仅提高了开发效率,也减少了因手写代码导致...

    能完美生成中文注释的mybatis-generator

    Mybatis Generator是一款强大的自动化代码生成工具,尤其在Java开发领域中广泛应用。它极大地提高了开发效率,减少了手动编写重复代码的工作量。本项目专注于解决如何在生成的实体类中添加中文注释的问题,使得代码...

    逆向工程mybatis-generator-1.3.2

    在这个场景中,“逆向工程mybatis-generator-1.3.2”指的是对MyBatis Generator 1.3.2版本的源代码或执行流程进行逆向分析。MyBatis Generator(MBG)是一个强大的工具,能够自动生成MyBatis接口和映射器XML文件,...

    generator-master.zip

    "generator-master.zip" 文件是一个与生成器(Generator)相关的压缩包,很可能包含了关于Python编程语言中的生成器(Generator)的示例代码、教程或者项目源码。生成器在Python中是一种特殊的迭代器,它们允许我们...

    generator工具包

    3. **Mapper接口和实现类生成**:generator会生成Mapper接口及其实现类,接口中定义了对应SQL操作的方法,实现类则包含了动态SQL的逻辑,这样我们就可以在业务代码中直接调用这些方法来执行数据库操作。 使用...

Global site tag (gtag.js) - Google Analytics