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(1, count))
{
using (DisposableObject childDisposableObject = new DisposableObject(string.Format("ChildDisposableObject{0}", number)))
{
//if (number == 4)
//{
// throw new Exception("异常。");
//}
if (number != 2)
{
yield return number * 10;
}
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(5))
{
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(-2);
d__.<>3__numbers = numbers;
return d__;
}

这里的<GetNumbers>d__0是个自动生成的类(看来这是高热量的语法糖,吃的是少了,程序集却发胖了!),它实现了IEnumerable<T>,IEnumerator<T>等接口,而上面方法其实就是返回了一个封装了迭代器块代码的计数对象而已,如果您仅仅调用了一下上面这个方法,它可能不会执行循环中的代码,除非触发了返回值的MoveNext方法,这就是传说中的延迟求值吧!

 

通过MSIL查看上面的foreach循环会调用MoveNext方法。

 

而循环里面的执行内容都在MoveNext方法里。

 

接着再看下using,也来个最简单的。

            using (DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject"))
{
Console.WriteLine("执行...");
//throw new Exception("异常。");
}

然后我们看一下对应的MSIL:

    .entrypoint
.maxstack 1
.locals init (
[0] class ConsoleApplication1.DisposableObject parentDisposableObject)
L_0000: ldstr "MainDisposableObject"
L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
L_000a: stloc.0
L_000b: ldstr "\u6267\u884c..."
L_0010: call void [mscorlib]System.Console::WriteLine(string)
L_0015: leave.s L_0021
L_0017: ldloc.0
L_0018: brfalse.s L_0020
L_001a: ldloc.0
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 1
.locals init (
[0] class ConsoleApplication1.DisposableObject parentDisposableObject)
L_0000: ldstr "MainDisposableObject"
L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
L_000a: stloc.0
L_000b: ldstr "\u6267\u884c..."
L_0010: call void [mscorlib]System.Console::WriteLine(string)
L_0015: leave.s L_001e
L_0017: ldloc.0
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#的编译器真是鬼斧神工啊!

 1 private bool MoveNext()
2 {
3 try
4 {
5 switch (this.<>1__state)
6 {
7 case 0:
8 this.<>1__state = -1;
9 this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject");
10 this.<>1__state = 1;
11 this.<>7__wrap5 = Enumerable.Range(1, this.count).GetEnumerator();
12 this.<>1__state = 2;
13 while (this.<>7__wrap5.MoveNext())
14 {
15 this.<number>5__2 = this.<>7__wrap5.Current;
16 this.<childDisposableObject>5__3 = new DisposableObject(string.Format("ChildDisposableObject{0}", this.<number>5__2));
17 this.<>1__state = 3;
18 if (this.<number>5__2 == 4)
19 {
20 throw new Exception("异常。");
21 }
22 if (this.<number>5__2 == 2)
23 {
24 goto Label_00D0;
25 }
26 this.<>2__current = this.<number>5__2 * 10;
27 this.<>1__state = 4;
28 return true;
29 Label_00C7:
30 this.<>1__state = 3;
31 goto Label_00E8;
32 Label_00D0:
33 Console.WriteLine("循环{0}:else 内代码执行了 ", this.<number>5__2.ToString());
34 Label_00E8:
35 Console.WriteLine("循环{0}:else下面的代码执行了 ", this.<number>5__2.ToString());
36 this.<>m__Finally7();
37 }
38 this.<>m__Finally6();
39 this.<>m__Finally4();
40 break;
41
42 case 4:
43 goto Label_00C7;
44 }
45 return false;
46 }
47 fault
48 {
49 this.System.IDisposable.Dispose();
50 }
51 }
52
53
54
55
 1 public DisposableObject <parentDisposableObject>5__1;
2
3
4 public DisposableObject <childDisposableObject>5__3;
5
6
7 private void <>m__Finally4()
8 {
9 this.<>1__state = -1;
10 if (this.<parentDisposableObject>5__1 != null)
11 {
12 this.<parentDisposableObject>5__1.Dispose();
13 }
14 }
15
16 private void <>m__Finally7()
17 {
18 this.<>1__state = 2;
19 if (this.<childDisposableObject>5__3 != null)
20 {
21 this.<childDisposableObject>5__3.Dispose();
22 }
23 }
24
25 void IDisposable.Dispose()
26 {
27 switch (this.<>1__state)
28 {
29 case 1:
30 case 2:
31 case 3:
32 case 4:
33 try
34 {
35 switch (this.<>1__state)
36 {
37 case 2:
38 case 3:
39 case 4:
40 try
41 {
42 switch (this.<>1__state)
43 {
44 case 3:
45 case 4:
46 try
47 {
48 }
49 finally
50 {
51 this.<>m__Finally7();
52 }
53 break;
54 }
55 }
56 finally
57 {
58 this.<>m__Finally6();
59 }
60 break;
61 }
62 }
63 finally
64 {
65 this.<>m__Finally4();
66 }
67 break;
68
69 default:
70 return;
71 }
72 }
 
 
分类: .NET开发C#语法

using和yield return的更多相关文章

  1. 可惜Java中没有yield return

    项目中一个消息推送需求,推送的用户数几百万,用户清单很简单就是一个txt文件,是由hadoop计算出来的.格式大概如下: uid caller 123456 12345678901 789101 12 ...

  2. C#中的using和yield return混合使用

    最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IEnumerable<T>了.我的代码里还有 ...

  3. yield return的用法简介

    使用yield return 语句可一次返回一个元素. 迭代器的声明必须满足以下要求: 返回类型必须为 IEnumerable.IEnumerable<T>.IEnumerator 或 I ...

  4. yield return的作用

    测试1: using UnityEngine; using System.Collections; public class test1 : MonoBehaviour { // Use this f ...

  5. yield学习续:yield return迭代块在Unity3D中的应用——协程

    必读好文推荐: Unity协程(Coroutine)原理深入剖析 Unity协程(Coroutine)原理深入剖析再续 上面的文章说得太透彻,所以这里就记一下自己的学习笔记了. 首先要说明的是,协程并 ...

  6. C#中yield return用法分析

    这篇文章主要介绍了C#中yield return用法,对比使用yield return与不使用yield return的流程,更直观的分析了yield return的用法,需要的朋友可以参考下. 本文 ...

  7. C#中的yield return与Unity中的Coroutine(协程)(下)

    Unity中的Coroutine(协程) 估计熟悉Unity的人看过或者用过StartCoroutine() 假设我们在场景中有一个UGUI组件, Image: 将以下代码绑定到Image using ...

  8. C#中的yield return与Unity中的Coroutine(协程)(上)

    C#中的yield return C#语法中有个特别的关键字yield, 它是干什么用的呢? 来看看专业的解释: yield 是在迭代器块中用于向枚举数对象提供值或发出迭代结束信号.它的形式为下列之一 ...

  9. 12.C#yield return和yield break及实际应用小例(六章6.2-6.4)

    晚上好,各位.今天结合书中所讲和MSDN所查,聊下yield关键字,它是我们简化迭代器的关键. 如果你在语句中使用了yield关键字,则意味着它在其中出现的方法.运算符或get访问器是迭代器,通过使用 ...

  10. yield return 和 yield break

    //yield return 返回类型必须为 IEnumerable.IEnumerable<T>.IEnumerator 或 IEnumerator<T>. static I ...

随机推荐

  1. Android利用网络编程HttpClient批量上传(两)AsyncTask+HttpClient监测进展情况,并上传

    请尊重别人的劳动.转载请注明出处: Android网络编程之使用HttpClient批量上传文件(二)AsyncTask+HttpClient并实现上传进度监听 执行效果图: 我曾在<Andro ...

  2. cocos2dx 解释二具体的启动过程:内存管理和回调

    在上一篇的第二部分中.我们有一句代码待解释的: // Draw the Scene void CCDirector::drawScene(void) { -...     //tick before ...

  3. BZOJ 1025 SCOI2009 游戏 动态规划

    标题效果:特定n.行定义一个替代品1~n这种更换周期发生后,T次要(T>0)返回到原来的顺序 找到行的所有可能的数 循环置换分解成若干个,然后行位移数是这些周期的长度的最小公倍数 因此,对于一些 ...

  4. crawler_java应用集锦9:httpclient4.2.2的几个常用方法,登录之后访问页面问题,下载文件_设置代理

    在工作中要用到android,然后进行网络请求的时候,打算使用httpClient. 总结一下httpClient的一些基本使用. 版本是4.2.2. 使用这个版本的过程中,百度很多,结果都是出现的o ...

  5. Log4j2 简明教程

    一.概述 log4j2官方文档内容非常多,要一次性了解全部是不可能的.正确的步骤应当是先了解最常见的配置,当发现原有知识无法解决问题,再重新查看文档看有没有合适的配置.下面将从文件结构入手,再到简单的 ...

  6. 使用Visual Studio创建映像向导(Image Sprite)——Web Essential

    原版的:Creating Image Sprite in Visual Studio - Web Essential 译者注:有关图片精灵的信息请參阅http://baike.baidu.com/vi ...

  7. 使用微软 URL Rewrite Module 开启IIS伪静态

    原文 使用微软 URL Rewrite Module 开启IIS伪静态 在IIS5和IIS6时代,我们使用URL REWRITING可实现URL重写,使得WEB程序实现伪静态,但默认情况下只能实现.A ...

  8. iOS程序发布时出现your application is being uploaded解决办法

    当用Xcode发布app时候出现“your application is being uploaded”或者用Application Loader 一直出现“ 正在通过ITUNES STORE进行鉴定 ...

  9. ASP.NET状态服务及session丢失问题解决方案总结

    原文:ASP.NET状态服务及session丢失问题解决方案总结[转载] asp.net Session的实现: asp.net的Session是基于HttpModule技术做的,HttpModule ...

  10. WCF服务上应用protobuf

    WCF服务上应用protobuf Web api  主要功能: 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, updat ...