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. ABP领域层——仓储(Repositories)

    ABP领域层——仓储(Repositories) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是 ...

  2. HDU 2159 FATE (完全背包+有限尚需时日)()双费背包

     FATE Problem Description 近期xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务.久而久之xhd開始对杀怪产生的厌恶感,但又不得不通过杀怪来升 ...

  3. 控制执行流程——(Java学习笔记三)

    if-else     控制程序流程最基本的形式 格式: if(boolean - expresion){ statement } 或 if(boolean - expresion){ stateme ...

  4. 经典算法题每日演练——第十七题 Dijkstra算法

    原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...

  5. jquery 元素控制(附加元素/其他内容)引进和应用

    一个.在内部元素/外部附加元件 append,prepend:加入到该子元素  before,after:元素加入 html: <div id="content"> 在 ...

  6. 多层次的Json字符串转化为对象

    using Arvato.CRM.DataTrans.ConsoleHost.Model;using System;using System.Collections.Generic;using Sys ...

  7. hibernate 一对多关联关系(具体分析)

    在领域模型中, 类与类之间最普遍的关系就是关联关系. 在 UML 中, 关联是有方向的.  以 Customer 和 Order 为例: 一个用户能发出多个订单, 而一个订单仅仅能属于一个客户. 从 ...

  8. Samza/KafkaAnalysizing

    Apache Samza is a distributed stream processing framework. It uses Apache Kafka for messaging, and A ...

  9. 完全用Linux工作,抛弃windows

    录一篇旧文 作者:王垠 完全用Linux工作,抛弃windows 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果 ...

  10. java阅读器hdfs单纯demo

    周围环境:eclipse + eclipse hadoop插入. hadoop + rhel6.4 package test; import java.io.IOException; import j ...