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[] { , , , ,};
List<int> list = new List<int>();
foreach (int item in arr)
{
if (item < )
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[] { ,,,,};
foreach (var item in arr)
{
yield return item;
if (item == )
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 = -;
} private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case :
this.<>1__state = -;
this.<arr>5__1 = new int[] { , , , , };
this.<>1__state = ;
this.<>7__wrap4 = this.<arr>5__1;
this.<>7__wrap5 = ;
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 = ;
return true;
Label_0090:
this.<>1__state = ;
if (this.<item>5__2 == )
{
this.System.IDisposable.Dispose();
break;
}
this.<>7__wrap5++;
}
this.<>m__Finally3();
break; case :
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 :
case :
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
.locals init (
[] class ConsoleApplication1.Program/<GetResultByYield>d__0 d__,
[] class [mscorlib]System.Collections.IEnumerator enumerator)
L_0000: ldc.i4.
L_0001: newobj instance void ConsoleApplication1.Program/<GetResultByYield>d__0::.ctor(int32)
L_0006: stloc.
L_0007: ldloc.
L_0008: stloc.
L_0009: br.s L_000b
L_000b: ldloc.
L_000c: ret
}

3.单例模式

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

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

声明:本文原创发表于博客园,作者为方小白,如有错误欢迎指出 。本文未经作者许可不许转载,否则视为侵权。

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

  1. 最适合作为Java基础面试题之Singleton模式

    看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...

  2. C#基础知识 yield与foreach

    什么时候可以使用yield的关键字来定义迭代器? 迭代器的返回类型必须是IEnumerable.IEnumerable<T>.IEnumerator 或 IEnumerator<T& ...

  3. Python基础之yield,匿名函数,包与re模块

    一.表达式形式的yield 1.另外一种形式的yield def deco(func): def wrapper(*arges, **kwargs): res = func(*arges, **kwa ...

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

    本文实例讲述了laravel5.1框架基础之Blade模板继承简单使用方法.分享给大家供大家参考,具体如下: 模板继承什么用? 自然是增强基础页面的复用,有利于页面文档的条理,也便于更改多处使用的内容 ...

  5. 读spring源码(二)-XmlBeanDefinitionReader-解析BeanDefinition

    上次说到ApplicationContext加载BeanDefinition时会创建一个XmlBeanDefinitionReader,将XML解析.BeanDefinition加载委托给XmlBea ...

  6. Java面试题 OOAD & UML+XML+SQL+JDBC & Hibernate

    二.OOA/D 与UML 部分:(共6 题:基础2 道,中等难度4 道) 96.UML 是什么?常用的几种图?[基础] 答:UML 是标准建模语言:常用图包括:用例图,静态图(包括类图.对象图和包图) ...

  7. java 面试大全

    一.CoreJava 部分: 基础及语法部分: 1.面向对象的特征有哪些方面? [基础] 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地 ...

  8. laravel中建立公共视图的方法

    1.用法概要 @include('common.header') 包含子视图 @extends('article.common.base') 继承基础模板 @yield('content') 视图占位 ...

  9. 【Unity3D基础教程】给初学者看的Unity教程(七):在Unity中构建健壮的单例模式(Singleton)

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点推荐.谢谢! 该博客中的代码均出自我的开源项目 : 迷你微信 ...

随机推荐

  1. Effective Java 77 For instance control, prefer enum types to readResolve

    The readResolve feature allows you to substitute another instance for the one created by readObject ...

  2. 从SqlServer现有数据生成Insert脚本

    步骤1,打开"Generate and Publish Objects"向导.右键点击要导出数据的数据库,选择Taks->GenerateScript 步骤2,选择要导出数据 ...

  3. 烂泥:centos安装及配置DHCP服务器

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 有关DHCP服务器的配置一直打算学习,这几天终于抽出时间来专门学习这个知识点. DHCP:动态主机配置协议,在此就不多做介绍.不清楚的童鞋,可以去百度下 ...

  4. 烂泥:KVM中安装Windows Server 2008 R2系统

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 在前一篇文章中,我介绍了有关在KVM中的安装Centos系统.接下来,就来介绍如何在KVM中安装Windows系统. 注意:在此我安装的是windows ...

  5. ubuntu自带的gedit编辑器添加Markdown预览插件

    gedit安装Markdown Preview Ubuntu自带的gedit编辑器也是有很强大的功能的,且支持插件的安装.对于喜欢用Markdown的我来说,这当然是很好的了,gedit本身 就支持M ...

  6. tomcat发布记录

    web项目发布详细步骤 服务器 tomcat服务器1.删除webapps文件夹里面的项目war包-->ifm.war(项目war名称)2.把项目的ifm.war放到webapps里面3.删除we ...

  7. Half Wavelength Dipole Antenna

    Reference : 1. wikipedia The dipole antenna is the simplest and most widely used class of antenna.It ...

  8. NP完全问题 NP-Completeness

    原创翻译加学习笔记,方便国人学习算法知识! 原文链接http://www.geeksforgeeks.org/np-completeness-set-1/ 我们已经找到很多很高效的算法来解决很难得问题 ...

  9. Codeforces Round #267 Div2 C George and Job --DP

    题意:把长度为n的序列分成k个m长的连续小序列,这些连续小序列的和最大是多少. 解法:显然DP. 定义: dp[i][j] 为前 i 个元素分成j个m端,且 i 是第j个的末尾的最大和. 那么有: d ...

  10. 《The Django Book》实战--第二章--动态网页基础

    这章演示了一些最基本的Django开发动态网页的实例,由于版本不一样,我用的是Django 1.,6.3,有些地方按书上的做是不行的,所以又改了一些,写出来让大家参考. 这是一个用python写的一个 ...