C#基础之yield与Singleton

1.实例解析yiled的作用

  最近参加java笔试题第一次见到yield这个关键字,既然遇见了那肯定要掌握,下面是C#中关于yield关键字的总结。yield这个关键字作用于迭代器块中,其最本质的功能有2个:一是“依次”向枚举对象提供值,二是发出迭代结束信号。这两个功能对应的语句分别是yield return和yield break。下面有2个小例子,分别没有使用yield和有使用yield。先来看第一个,当我调试时显然执行到GetResult()方法时将会跳转到方法内部并且执行完,接着再去执行输出当前值语句。从结果可以看出第一个是0,说明返回的枚举数所在的位置在集合中是0,接着才是我想要的遍历数据,也就是说只有调用MoveNext()后枚举数才会继续向前移动得到下一个值,但是此时数据已全部加载到内存。再来看第二个例子,当我调试到GetResultByYield()方法时我想进入到这个方法内部结果发现直接执行下一句,根本就没有进入到GetResultByYield()方法内部。此时发现result.Current是null,再加上前面根本都没执行方法内部的代码,因此我猜测此时集合都是空的。继续调试,当执行MoveNext()方法时才去执行GetResultByYield(),接着执行到yield return随即返回main()方法输出枚举数所代表的集合中的值。

  从上面可以看到只有调用MoveNext()需要用的时候才去执行方法来获得结果,不用的时候并不会有任何结果。这个地方编译器会有一个状态机用来保存迭代器的状态,以保证for循环时是从上一次yield return停止的状态继续执行。这个过程就好比小方要喝一升的水,如果它用一个一升的杯子喝那么他要准备一个一升的容器去饮水机装满一升的水。如果小方喝到一半喝不完了,那接下来剩下的水则将被回收,这样无论能不能喝完都必须准备好一升的水,就像第一个例子。现在让杯子的容积缩小为0.2升,小方喝完一杯后再去饮水机去打水,每次只喝0.2升。这样只有他要去喝的时候才去打水,如果他喝到一半不想喝了显然浪费的水比第一种方式多,这就像第二个例子。最后根据条件不再需要数据便可调用yield return来跳出while循环,如果不写yield break仍然可以正常结束迭代。

/// <summary>
    /// 不使用yield的时候
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            //得到一个迭代结果
            var result = GetResult();
            //输出当前的值
            Console.WriteLine(result.Current);
            Console.WriteLine("开始遍历");
            while (result.MoveNext())
            {
                Console.WriteLine(result.Current);
            }
            Console.WriteLine("遍历结束");
            Console.ReadLine();
        }
        //不使用yield来进行迭代
        static IEnumerator<int> GetResult()
        {
            var arr = new int[] { 1, 6, 8, 12,15};
            List<int> list = new List<int>();
            foreach (int item in arr)
            {
                if (item < 12)
                    list.Add(item);
            }
            return list.GetEnumerator();
        }

    }

    /// <summary>
    /// 使用yield关键字
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            //得到一个迭代结果
            var result = GetResultByYield();
            //输出当前的值
            Console.WriteLine(result.Current);
            Console.WriteLine("开始遍历");
            while (result.MoveNext())
            {
                Console.WriteLine(result.Current);
            }
            Console.WriteLine("遍历结束");
            Console.ReadLine();
        }
        //使用yield来进行迭代
        static IEnumerator GetResultByYield()
        {
            var arr = new int[] { 1,6,8,12,15};
            foreach (var item in arr)
            {
                yield return item;
                if (item == 12)
                    yield break;
            }
        }

    }

输出结果如下:

2.深入yield

  将上面第二个例子放入Reflector工具中,便得到了下面三段代码。第一段是完整的Pragrom类的C#代码,第二段是<GetResultByYield>d__0密封类的C#展开代码,第三段是GetResultByYield()方法的IL代码。在第一段代码中可以看到系统自动生成了一个<GetResultByYield>d__0密封类,它里面声明了一些名字很奇怪的字段,不过我们可以很清楚的看到这个类里面有最重要的MoveNext()方法和Current属性。第二段代码则是这个密封类的C#展开代码,到这里不知道读者有没有和我当初一样的疑问:为什么要自动生成一个密封类呢?答案就在第三段代码中,可以看到在GetResultByYield()方法中并没有遍历数组,甚至都没有看到创建数组的newarr指令,而是newobj创建了<GetResultByYield>d__0密封类的实例对象。这也正是前面调试的时候为什么根本就没进去GetResultByYield()方法的原因,因为真真的实现代码是在密封类里面的MoveNext()方法中。前面还提到yield是按需所取,因此需要一个状态机来记录每次yield return的状态。

  在MoveNext()方法中由于密封类构造函数传进去的是一个0(在第三段代码中可以看到),因此第一次进入到MoveNext方法时this.<>__state=0。此时current字段由于没赋值因此就是null了。接着创建数组并开始一个while循环(原来foreach就是while循环),在循环中给current字段赋值并让state字段值为2,最后返回true。拿Current属性时就是拿while循环中给current赋的值,再次进入这个方法内此时state等于2于是跳转到Label_0090,也就是进入while语句块中继续循环,这就是按需所取的原理。当遇到yield break后会先执行Dispose释放资源,再执行break语句跳出循环。可以看到上述这个过程就是一个状态机,而这个密封类是为建立一个状态机来生成的,现在我们自己也可以写出一个状态机了。

internal class Program
{
    // Methods
    public Program();
    private static IEnumerator GetResultByYield();
    private static void Main(string[] args);

    // Nested Types
    [CompilerGenerated]
    private sealed class <GetResultByYield>d__0 : IEnumerator<object>, IEnumerator, IDisposable
    {
        // Fields
        private int <>1__state;
        private object <>2__current;
        public int[] <>7__wrap4;
        public int <>7__wrap5;
        public int[] <arr>5__1;
        public int <item>5__2;

        // Methods
        [DebuggerHidden]
        public <GetResultByYield>d__0(int <>1__state);
        private void <>m__Finally3();
        private bool MoveNext();
        [DebuggerHidden]
        void IEnumerator.Reset();
        void IDisposable.Dispose();

        // Properties
        object IEnumerator<object>.Current { [DebuggerHidden] get; }
        object IEnumerator.Current { [DebuggerHidden] get; }
    }
}

private sealed class <GetResultByYield>d__0 : IEnumerator<object>, IEnumerator, IDisposable
{
    // Fields
    private int <>1__state;
    private object <>2__current;
    public int[] <>7__wrap4;
    public int <>7__wrap5;
    public int[] <arr>5__1;
    public int <item>5__2;

    // Methods
    [DebuggerHidden]
    public <GetResultByYield>d__0(int <>1__state)
    {
        this.<>1__state = <>1__state;
    }

    private void <>m__Finally3()
    {
        this.<>1__state = -1;
    }

    private bool MoveNext()
    {
        try
        {
            switch (this.<>1__state)
            {
                case 0:
                    this.<>1__state = -1;
                    this.<arr>5__1 = new int[] { 1, 6, 8, 12, 15 };
                    this.<>1__state = 1;
                    this.<>7__wrap4 = this.<arr>5__1;
                    this.<>7__wrap5 = 0;
                    while (this.<>7__wrap5 < this.<>7__wrap4.Length)
                    {
                        this.<item>5__2 = this.<>7__wrap4[this.<>7__wrap5];
                        this.<>2__current = this.<item>5__2;
                        this.<>1__state = 2;
                        return true;
                    Label_0090:
                        this.<>1__state = 1;
                        if (this.<item>5__2 == 12)
                        {
                            this.System.IDisposable.Dispose();
                            break;
                        }
                        this.<>7__wrap5++;
                    }
                    this.<>m__Finally3();
                    break;

                case 2:
                    goto Label_0090;
            }
            return false;
        }
        fault
        {
            this.System.IDisposable.Dispose();
        }
    }

    [DebuggerHidden]
    void IEnumerator.Reset()
    {
        throw new NotSupportedException();
    }

    void IDisposable.Dispose()
    {
        switch (this.<>1__state)
        {
            case 1:
            case 2:
                this.<>m__Finally3();
                break;
        }
    }

    // Properties
    object IEnumerator<object>.Current
    {
        [DebuggerHidden]
        get
        {
            return this.<>2__current;
        }
    }

    object IEnumerator.Current
    {
        [DebuggerHidden]
        get
        {
            return this.<>2__current;
        }
    }
}

    .method private hidebysig static class [mscorlib]System.Collections.IEnumerator GetResultByYield() cil managed
    {
        .maxstack 1
        .locals init (
            [0] class ConsoleApplication1.Program/<GetResultByYield>d__0 d__,
            [1] class [mscorlib]System.Collections.IEnumerator enumerator)
        L_0000: ldc.i4.0
        L_0001: newobj instance void ConsoleApplication1.Program/<GetResultByYield>d__0::.ctor(int32)
        L_0006: stloc.0
        L_0007: ldloc.0
        L_0008: stloc.1
        L_0009: br.s L_000b
        L_000b: ldloc.1
        L_000c: ret
    }

3.单例模式

  单例模式没什么好说的,当然如果深挖应该也是大有学问,其中我觉得比较好的一种写法如下。单例模式的代码我看过多次不过却没怎么写,结果真真写的时候再加上时间又有点紧最后写的一塌糊涂。以后写代码要兴平气和地去写,急躁的状态写不出什么好代码。当然总会有烦躁的时候,所以只能多写代码来让自己写出高质量的代码成为一种习惯!

    class A
    {
        private static A instance = new A();
        public static A Instance
        {
            get { return A.instance; }
        }
    }

时间: 2024-10-24 22:14:24

C#基础之yield与Singleton的相关文章

C#基础知识 yield与foreach

什么时候可以使用yield的关键字来定义迭代器? 迭代器的返回类型必须是IEnumerable.IEnumerable<T>.IEnumerator 或 IEnumerator<T> 迭代器的入参不能包括ref或out类型的参数 首先,我们定义一个简单的遍历. static void Main(string[] args) { List<Person> persons = new List<Person>(); persons.Add(new Person

laravel5.1框架基础之Blade模板继承简单使用方法分析

模板继承什么用? 自然是增强基础页面的复用,有利于页面文档的条理,也便于更改多处使用的内容,如页头.页脚 1.用法概要 @include('common.header') 包含子视图 @extends('article.common.base') 继承基础模板 @yield('content') 视图占位符 @section('content') @endsection继承模板后向视图占位符中填入内容 {{-- 注释 --}} Blade模板中注释的使用 2.具体使用 2.1 新建Article

java web 开发三剑客 -------电子书

Internet,人们通常称为因特网,是当今世界上覆盖面最大和应用最广泛的网络.根据英语构词法,Internet是Inter + net,Inter-作为前缀在英语中表示“在一起,交互”,由此可知Internet的目的是让各个net交互.所以,Internet实质上是将世界上各个国家.各个网络运营商的多个网络相互连接构成的一个全球范围内的统一网,使各个网络之间能够相互到达.各个国家和运营商构建网络采用的底层技术和实现可能各不相同,但只要采用统一的上层协议(TCP/IP)就可以通过Internet

C#基础复习IEnumerable(和 yield return的使用滴呀 )

IEnumerable 真是基础中的基础,然而..... 我们直接来看看这个接口的实现吧: 它是一个公开枚举数,该枚举数支持在非泛型集合上进行简单的迭代.换句话说,对于所有数组的遍历,都来自IEnumerable,那么我们就可以利用这个特性,来定义一个能够遍历xxxxxx的通用方法 先看我们的经典实例1: using System; using System.Collections; using System.Collections.Generic; using System.Linq; usi

零基础学python-19.5 重放迭代器:生成器yield

这一章节我们来讨论一些生成器yield 1.yield的特性:延迟结果创建 * 生成器函数:也是使用def定义,但是使用yield返回,而且每次返回只是返回一个结果,在每次产生结果之间挂起和继续它们的状态(就是当返回有三个结果,第一个结果返回了,但是第二个结果没有返回,但是由于是yield,它记录下返回第一个结果时所有作用域以及变量的状态,因此,在当需要返回第二个结果的时候,它将会继续刚才的状态计算下去). >>> def test(N): for x in range(N): yiel

python基础:迭代器、生成器(yield)详细解读

1. 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退. 1.1 使用迭代器的优点 对于原生支持随机访问的数据结构(如tuple.list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值).但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式. 另外,迭代器的一大优点是不要求事

python基础:带有yield关键字函数调用后每一步详解

1 #-*- coding:utf-8 -*- 2 __author__ = 'Administrator' 3 4 from collections import deque 5 6 def search(lines, pattern, history=5): 7 previons_line = deque(maxlen=history) 8 for line in lines: 9 if pattern in line: 10 yield line, previons_line 11 pre

java基础:线程方法之yield方法

一:看程序 二:分析 (1)当i能被10整除的时候,让出线程. if(i % 10 == 0){ yield(); } (2)MyThread(String name) { <span style="white-space:pre"> </span>super(name); } 这个构造方法,可以给线程起名字. 创建线程的时候,可以指定名字MyThread t1 = new MyThread("t1");

Java基础复习(2) 多线程相关 sleep() yield() wait() join()等方法简略说明

2015-10-23 今天回顾的是多线程的部分,对于线程编程常用的几个方法稍作整理和区分. 1.join方法 (之前总是记的迷迷糊糊,重新看了一遍,算是基本理清了orz) join方法属于线程,意思是把指定线程加入到当前线程执行序列,可以让本来是交替执行的两个线程合并成顺序执行的线程.例如:同时执行线程thread-A和线程thread-B,此时在线程thread-B中调用线程thread-A的join()方法,结果就是直到线程thread-A执行完毕,线程thread-B才继续执行. 使用: