C#中的using和yield return混合使用
最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IEnumerable<T>了。我的代码里还有很多需要Dispose的对象,所以又用了几个using。写着写着我有点心虚了——这样混合使用靠谱吗?
今天我花时间研究一下,并在这里作个笔记,跟大家分享。笔者水平有限,有哪些理解错误或做的不到位的地方,还请各位专家点拨。
这是我写的方法,循环外面一个using,整个方法里代码执行后释放一个对象。循环里面又一个using, 每次循环yield return后要释放一个对象。那是不是任何情况下这些[被创建了的需要释放的]DisposableObject对象最后都会被释放呢?
private static IEnumerable<int> GetNumbers(int count)
{
using (DisposableObject parentDisposableObject = new DisposableObject("ParentDisposableObject"))
{
foreach (int number in Enumerable.Range(, count))
{
using (DisposableObject childDisposableObject = new DisposableObject(string.Format("ChildDisposableObject{0}", number)))
{
//if (number == 4)
//{
// throw new Exception("异常。");
//}
if (number != )
{
yield return number * ;
}
else
{
Console.WriteLine(" 循环{0} else 代码执行了", number.ToString());
}
Console.WriteLine(" 循环{0}else下面的代码执行了", number.ToString());
}
}
}
}
}
需要释放资源的类定义如下,创建对象和释放时都有输出。
class DisposableObject : IDisposable
{
private string _value;
public DisposableObject(string value)
{
_value = value;
Console.WriteLine("Create Object {0}", _value);
}
public void Dispose()
{
Console.WriteLine("Disposable Object {0}", _value);
}
}
这里调用下:
static void Main(string[] args)
{
foreach (int number in GetNumbers())
{
Console.WriteLine("结果 {0}", number.ToString());
}
}
看看运行结果:
我们可以看到:1、循环外面的对象和循环里面的DisposableObject对象都被释放了,这个让我很高兴,要的就是这个效果;2,如果yield return后面还有代码,[yield] return后还会继续执行;3,if-else有作用,不满足条件可以不把该项作为结果返回,不想执行某段代码可以放{}里。这个运行的结果我很满意,就是我想要的!
下面我把抛异常的代码注释去掉,看看循环内抛出的异常后能否正常释放对象。
结果很完美,担忧是多余的,该释放的DisposableObject对象都被释放了!
那么我们简单研究下yield return吧,我写了下面最简单的代码:
private static IEnumerable<int> GetNumbers(int[] numbers)
{
foreach (int number in numbers)
{
yield return number*10;
}
}
把项目编译再反编译成C#2.0,发现代码变成了这个样子:
private static IEnumerable<int> GetNumbers(int[] numbers)
{
<GetNumbers>d__0 d__ = new <GetNumbers>d__0(-);
d__.<>3__numbers = numbers;
return d__;
}
这里的<GetNumbers>d__0是个自动生成的类(看来这是高热量的语法糖,吃的是少了,程序集却发胖了!),它实现了IEnumerable<T>,IEnumerator<T>等接口,而上面方法其实就是返回了一个封装了迭代器块代码的计数对象而已,如果您仅仅调用了一下上面这个方法,它可能不会执行循环中的代码,除非触发了返回值的MoveNext方法,这就是传说中的延迟求值吧!
[CompilerGenerated]
private sealed class <GetNumbers>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private int <>2__current;
public int[] <>3__numbers;
public int[] <>7__wrap3;
public int <>7__wrap4;
private int <>l__initialThreadId;
public int <number>5__1;
public int[] numbers; // Methods
[DebuggerHidden]
public <GetNumbers>d__0(int <>1__state);
private void <>m__Finally2();
private bool MoveNext();
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose(); // Properties
int IEnumerator<int>.Current { [DebuggerHidden] get; }
object IEnumerator.Current { [DebuggerHidden] get; }
} Expand Methods
通过MSIL查看上面的foreach循环会调用MoveNext方法。
entrypoint
.maxstack
.locals init (
[] int32 number,
[] class [mscorlib]System.Collections.Generic.IEnumerator`<int32> CS$$)
L_0000: ldc.i4.
L_0001: call class [mscorlib]System.Collections.Generic.IEnumerable`<int32> ConsoleApplication1.Program::GetNumbers(int32)
L_0006: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`<!> [mscorlib]System.Collections.Generic.IEnumerable`<int32>::GetEnumerator()
L_000b: stloc.
L_000c: br.s L_0026
L_000e: ldloc.
L_000f: callvirt instance ! [mscorlib]System.Collections.Generic.IEnumerator`<int32>::get_Current()
L_0014: stloc.
L_0015: ldstr "\u7ed3\u679c\uff1a{0}"
L_001a: ldloca.s number
L_001c: call instance string [mscorlib]System.Int32::ToString()
L_0021: call void [mscorlib]System.Console::WriteLine(string, object)
L_0026: ldloc.
L_0027: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_002c: brtrue.s L_000e
L_002e: leave.s L_003a
L_0030: ldloc.
L_0031: brfalse.s L_0039
L_0033: ldloc.
L_0034: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0039: endfinally
L_003a: ret
.try L_000c to L_0030 finally handler L_0030 to L_003a
而循环里面的执行内容都在MoveNext方法里。
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case :
this.<>1__state = -;
this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject");
this.<>1__state = ;
this.<>7__wrap5 = Enumerable.Range(, this.count).GetEnumerator();
this.<>1__state = ;
while (this.<>7__wrap5.MoveNext())
{
this.<number>5__2 = this.<>7__wrap5.Current;
this.<childDisposableObject>5__3 = new DisposableObject(string.Format("ChildDisposableObject{0}", this.<number>5__2));
this.<>1__state = ;
if (this.<number>5__2 == )
{
throw new Exception("异常。");
}
if (this.<number>5__2 == )
{
goto Label_00D0;
}
this.<>2__current = this.<number>5__2 * ;
this.<>1__state = ;
return true;
Label_00C7:
this.<>1__state = ;
goto Label_00E8;
Label_00D0:
Console.WriteLine("循环{0}:else 内代码执行了 ", this.<number>5__2.ToString());
Label_00E8:
Console.WriteLine("循环{0}:else下面的代码执行了 ", this.<number>5__2.ToString());
this.<>m__Finally7();
}
this.<>m__Finally6();
this.<>m__Finally4();
break; case :
goto Label_00C7;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
接着再看下using,也来个最简单的。
using (DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject"))
{
Console.WriteLine("执行...");
//throw new Exception("异常。");
}
然后我们看一下对应的MSIL:
.entrypoint
.maxstack
.locals init (
[] class ConsoleApplication1.DisposableObject parentDisposableObject)
L_0000: ldstr "MainDisposableObject"
L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
L_000a: stloc.
L_000b: ldstr "\u6267\u884c..."
L_0010: call void [mscorlib]System.Console::WriteLine(string)
L_0015: leave.s L_0021
L_0017: ldloc.
L_0018: brfalse.s L_0020
L_001a: ldloc.
L_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0020: endfinally
L_0021: ret
.try L_000b to L_0017 finally handler L_0017 to L_0021
再换一种C#写法试试:
DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject");
try
{
Console.WriteLine("执行...");
//throw new Exception("异常。");
}
finally
{
parentDisposableObject.Dispose();
}
对应的MSIL代码:
.entrypoint
.maxstack
.locals init (
[] class ConsoleApplication1.DisposableObject parentDisposableObject)
L_0000: ldstr "MainDisposableObject"
L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
L_000a: stloc.
L_000b: ldstr "\u6267\u884c..."
L_0010: call void [mscorlib]System.Console::WriteLine(string)
L_0015: leave.s L_001e
L_0017: ldloc.
L_0018: callvirt instance void ConsoleApplication1.DisposableObject::Dispose()
L_001d: endfinally
L_001e: ret
.try L_000b to L_0017 finally handler L_0017 to L_001e
看看两段MSIL多像啊,特别是最后一句!
最后我们看看yield return 和 using混合使用时,自动生成的<GetNumbers>d__0类是如何保证需要释放资源的DisposableObject对象被释放的,看后我不禁感慨:C#的编译器真是鬼斧神工啊!
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case :
this.<>1__state = -;
this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject");
this.<>1__state = ;
this.<>7__wrap5 = Enumerable.Range(, this.count).GetEnumerator();
this.<>1__state = ;
while (this.<>7__wrap5.MoveNext())
{
this.<number>5__2 = this.<>7__wrap5.Current;
this.<childDisposableObject>5__3 = new DisposableObject(string.Format("ChildDisposableObject{0}", this.<number>5__2));
this.<>1__state = ;
if (this.<number>5__2 == )
{
throw new Exception("异常。");
}
if (this.<number>5__2 == )
{
goto Label_00D0;
}
this.<>2__current = this.<number>5__2 * ;
this.<>1__state = ;
return true;
Label_00C7:
this.<>1__state = ;
goto Label_00E8;
Label_00D0:
Console.WriteLine("循环{0}:else 内代码执行了 ", this.<number>5__2.ToString());
Label_00E8:
Console.WriteLine("循环{0}:else下面的代码执行了 ", this.<number>5__2.ToString());
this.<>m__Finally7();
}
this.<>m__Finally6();
this.<>m__Finally4();
break; case :
goto Label_00C7;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
public DisposableObject <parentDisposableObject>5__1; public DisposableObject <childDisposableObject>5__3; private void <>m__Finally4()
{
this.<>1__state = -;
if (this.<parentDisposableObject>5__1 != null)
{
this.<parentDisposableObject>5__1.Dispose();
}
} private void <>m__Finally7()
{
this.<>1__state = ;
if (this.<childDisposableObject>5__3 != null)
{
this.<childDisposableObject>5__3.Dispose();
}
} void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case :
case :
case :
case :
try
{
switch (this.<>1__state)
{
case :
case :
case :
try
{
switch (this.<>1__state)
{
case :
case :
try
{
}
finally
{
this.<>m__Finally7();
}
break;
}
}
finally
{
this.<>m__Finally6();
}
break;
}
}
finally
{
this.<>m__Finally4();
}
break; default:
return;
}
}
C#中的using和yield return混合使用的更多相关文章
- C#中,什么时候用yield return
yield关键字用于遍历循环中,yield return用于返回IEnumerable<T>,yield break用于终止循环遍历. 有这样的一个int类型的集合: static Lis ...
- using和yield return
C#中的using和yield return混合使用 最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IE ...
- 可惜Java中没有yield return
项目中一个消息推送需求,推送的用户数几百万,用户清单很简单就是一个txt文件,是由hadoop计算出来的.格式大概如下: uid caller 123456 12345678901 789101 12 ...
- yield学习续:yield return迭代块在Unity3D中的应用——协程
必读好文推荐: Unity协程(Coroutine)原理深入剖析 Unity协程(Coroutine)原理深入剖析再续 上面的文章说得太透彻,所以这里就记一下自己的学习笔记了. 首先要说明的是,协程并 ...
- C#中yield return用法分析
这篇文章主要介绍了C#中yield return用法,对比使用yield return与不使用yield return的流程,更直观的分析了yield return的用法,需要的朋友可以参考下. 本文 ...
- C#中的yield return与Unity中的Coroutine(协程)(下)
Unity中的Coroutine(协程) 估计熟悉Unity的人看过或者用过StartCoroutine() 假设我们在场景中有一个UGUI组件, Image: 将以下代码绑定到Image using ...
- C#中的yield return与Unity中的Coroutine(协程)(上)
C#中的yield return C#语法中有个特别的关键字yield, 它是干什么用的呢? 来看看专业的解释: yield 是在迭代器块中用于向枚举数对象提供值或发出迭代结束信号.它的形式为下列之一 ...
- C#中的yield return用法演示源码
下边代码段是关于C#中的yield return用法演示的代码. using System;using System.Collections;using System.Collections.Gene ...
- Unity中yield return null和yield return WaitForEndOfFrame的区别
2017/07/04修改 - 对WaitForEndOfFrame的LateUpdate时序进行说明. 测试结论: 1.如果只是等待下一帧执行,用yield return null即可.调用顺序在Up ...
随机推荐
- GridView控件RowDataBound事件中获取列字段途径
今天不知道怎么回事怎么也找不到gridview列中的控件,关键是其为编辑时隐藏域中的控件,取值就很成问题了,网上搜了很到,找到这个比较经典的东东了,可能大家都知道,但很少对比整理到一起,有多种方法可以 ...
- Arcgis Server发布服务
提到Arcgis Server 服务的发布,做起来貌似很简单,就算电脑再卡,只要鼠标还能点,一个小时肯定能搞定,但是当你遇到问题的时候,就头大了,也许搞上个一两天都摸不着头脑,最后你采取的措施可能是一 ...
- Quartus II中的Waring(转)
1.Found clock-sensitive change during active clock edge at time <time> on register "<n ...
- const 限定符
1.定义const对象 const限定符把一个对象转换成一个常量 const int Bufsize = 512; 定义Bufsize 为常量并初始化为512.变量Bufsize仍然是一个左值,但是不 ...
- Loadrunner ---集合点设置
测试场景,实现用户登录的200并发: 1.录制登录退出脚本,且登录退出放aciton中 2.在登录的地方设置集合点 设置集合点有如下4中方式: 1)在要插入集合点的地方,右击鼠标按如下图操作:
- <textarea>没有内容时,按回车键,设置光标不换行
$("textarea").val($("textarea").val().replace(/\n/g,""));
- 收藏网址 ios开源库
http://www.csdn.net/article/2015-07-21/2825264-27-ios-open-source-libraries/1
- 什么是业务运维,企业如何实现互联网+业务与IT的融合
业务运维并不是一个新概念,针对传统信息架构提出的业务服务管理就是把以业务为核心的IT系统与IT基础设施性能进行整合运维的解决方案.然而随着互联网+转型的不断推进,基础设施的智能化和广泛云化成为IT发展 ...
- B. Checkout Assistant 01背包变形
http://codeforces.com/problemset/problem/19/B 对于每个物品,能偷多ti个,那么先让ti + 1, 表示选了这个东西后,其实就是选了ti + 1个了.那么只 ...
- Javascript中DOM的练习
第一个题:html计时器 方法一: <body onLoad="show()" > <div id="b"></div> & ...