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[] { , , , ,};
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的更多相关文章
- 最适合作为Java基础面试题之Singleton模式
看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...
- C#基础知识 yield与foreach
什么时候可以使用yield的关键字来定义迭代器? 迭代器的返回类型必须是IEnumerable.IEnumerable<T>.IEnumerator 或 IEnumerator<T& ...
- Python基础之yield,匿名函数,包与re模块
一.表达式形式的yield 1.另外一种形式的yield def deco(func): def wrapper(*arges, **kwargs): res = func(*arges, **kwa ...
- laravel5.1框架基础之Blade模板继承简单使用方法分析
本文实例讲述了laravel5.1框架基础之Blade模板继承简单使用方法.分享给大家供大家参考,具体如下: 模板继承什么用? 自然是增强基础页面的复用,有利于页面文档的条理,也便于更改多处使用的内容 ...
- 读spring源码(二)-XmlBeanDefinitionReader-解析BeanDefinition
上次说到ApplicationContext加载BeanDefinition时会创建一个XmlBeanDefinitionReader,将XML解析.BeanDefinition加载委托给XmlBea ...
- Java面试题 OOAD & UML+XML+SQL+JDBC & Hibernate
二.OOA/D 与UML 部分:(共6 题:基础2 道,中等难度4 道) 96.UML 是什么?常用的几种图?[基础] 答:UML 是标准建模语言:常用图包括:用例图,静态图(包括类图.对象图和包图) ...
- java 面试大全
一.CoreJava 部分: 基础及语法部分: 1.面向对象的特征有哪些方面? [基础] 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地 ...
- laravel中建立公共视图的方法
1.用法概要 @include('common.header') 包含子视图 @extends('article.common.base') 继承基础模板 @yield('content') 视图占位 ...
- 【Unity3D基础教程】给初学者看的Unity教程(七):在Unity中构建健壮的单例模式(Singleton)
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点推荐.谢谢! 该博客中的代码均出自我的开源项目 : 迷你微信 ...
随机推荐
- Effective Java 24 Eliminate unchecked warnings
Note Eliminate every unchecked warning that you can. Set<Lark> exaltation = new HashSet(); The ...
- PHP读写XML文件的四种方法
PHP对XML文件进行读写操作的方法一共有四种,分别是:字符串方式直接读写.DOMDocument读写. XMLWrite写和XMLReader读.SimpleXML读写,本文将依次对这四种方法进行介 ...
- Web Farm 和Web Garden
这两个都是提高网站性能的服务器端技术 1.Web Farm:如果应用程序被多个服务器托管,这种情况就可以称作Web Farm. 2.Web Garden: 指的是一个应用程序可以分成多个进程(w3wp ...
- nginx 做负载均衡
最近正在研究Nginx,Nginx作为反向代理服务器,可以对Web服务器提供加速,并且具有负载均衡的功能. 首先我要在官网下载Nginx(http://nginx.org/en/download.ht ...
- uva 558 tree(不忍吐槽的题目名)——yhx
You are to determine the value of the leaf node in a given binary tree that is the terminal node of ...
- TestNG之执行顺序
如果很有个测试方法,并且这几个方法又有先后顺序,那么如果让TestNG按照自己想要的方法执行呢 一.通过Dependencies 1.在测试类中添加Dependencies @Test public ...
- 【CSS】颜色码对照表
英文代码 形像颜色 HEX格式 RGB格式 LightPink 浅粉色 #FFB6C1 255,182,193 Pink 粉红 #FFC0CB 255,192,203 Crimson 猩红 #DC14 ...
- leetcode-Maximum Subarray
https://leetcode.com/problems/maximum-subarray/ Find the contiguous subarray within an array (contai ...
- python中怎么查看当前工作目录和更改工作目录
查询当前目录:os.getcwd() 更改当前目录:os.chdir()
- 2014 Super Training #6 A Alice and Bob --SG函数
原题: ZOJ 3666 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3666 博弈问题. 题意:给你1~N个位置,N是最 ...