最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IEnumerable<T>了。我的代码里还有很多需要Dispose的对象,所以又用了几个using。写着写着我有点心虚了——这样混合使用靠谱吗?

今天我花时间研究一下,并在这里作个笔记,跟大家分享。笔者水平有限,有哪些理解错误或做的不到位的地方,还请各位专家点拨。

这是我写的方法,循环外面一个using,整个方法里代码执行后释放一个对象。循环里面又一个using, 每次循环yield return后要释放一个对象。那是不是任何情况下这些[被创建了的需要释放的]DisposableObject对象最后都会被释放呢?

  1. private static IEnumerable<int> GetNumbers(int count)
  2. {
  3. using (DisposableObject parentDisposableObject = new DisposableObject("ParentDisposableObject"))
  4. {
  5. foreach (int number in Enumerable.Range(, count))
  6. {
  7. using (DisposableObject childDisposableObject = new DisposableObject(string.Format("ChildDisposableObject{0}", number)))
  8. {
  9. //if (number == 4)
  10. //{
  11. // throw new Exception("异常。");
  12. //}
  13. if (number != )
  14. {
  15. yield return number * ;
  16. }
  17. else
  18. {
  19. Console.WriteLine(" 循环{0} else 代码执行了", number.ToString());
  20. }
  21. Console.WriteLine(" 循环{0}else下面的代码执行了", number.ToString());
  22. }
  23. }
  24. }
  25. }
  26. }

需要释放资源的类定义如下,创建对象和释放时都有输出。

  1. class DisposableObject : IDisposable
  2. {
  3. private string _value;
  4. public DisposableObject(string value)
  5. {
  6. _value = value;
  7. Console.WriteLine("Create Object {0}", _value);
  8. }
  9. public void Dispose()
  10. {
  11. Console.WriteLine("Disposable Object {0}", _value);
  12. }
  13. }

这里调用下:

  1. static void Main(string[] args)
  2. {
  3. foreach (int number in GetNumbers())
  4. {
  5. Console.WriteLine("结果 {0}", number.ToString());
  6. }
  7. }

看看运行结果:

我们可以看到:1、循环外面的对象和循环里面的DisposableObject对象都被释放了,这个让我很高兴,要的就是这个效果;2,如果yield return后面还有代码,[yield] return后还会继续执行;3,if-else有作用,不满足条件可以不把该项作为结果返回,不想执行某段代码可以放{}里。这个运行的结果我很满意,就是我想要的!

下面我把抛异常的代码注释去掉,看看循环内抛出的异常后能否正常释放对象。

结果很完美,担忧是多余的,该释放的DisposableObject对象都被释放了!

那么我们简单研究下yield return吧,我写了下面最简单的代码:

  1. private static IEnumerable<int> GetNumbers(int[] numbers)
  2. {
  3. foreach (int number in numbers)
  4. {
  5. yield return number*10;
  6. }
  7. }

把项目编译再反编译成C#2.0,发现代码变成了这个样子:

  1. private static IEnumerable<int> GetNumbers(int[] numbers)
  2. {
  3. <GetNumbers>d__0 d__ = new <GetNumbers>d__0(-);
  4. d__.<>3__numbers = numbers;
  5. return d__;
  6. }

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

  1. [CompilerGenerated]
  2. private sealed class <GetNumbers>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
  3. {
  4. // Fields
  5. private int <>1__state;
  6. private int <>2__current;
  7. public int[] <>3__numbers;
  8. public int[] <>7__wrap3;
  9. public int <>7__wrap4;
  10. private int <>l__initialThreadId;
  11. public int <number>5__1;
  12. public int[] numbers;
  13.  
  14. // Methods
  15. [DebuggerHidden]
  16. public <GetNumbers>d__0(int <>1__state);
  17. private void <>m__Finally2();
  18. private bool MoveNext();
  19. [DebuggerHidden]
  20. IEnumerator<int> IEnumerable<int>.GetEnumerator();
  21. [DebuggerHidden]
  22. IEnumerator IEnumerable.GetEnumerator();
  23. [DebuggerHidden]
  24. void IEnumerator.Reset();
  25. void IDisposable.Dispose();
  26.  
  27. // Properties
  28. int IEnumerator<int>.Current { [DebuggerHidden] get; }
  29. object IEnumerator.Current { [DebuggerHidden] get; }
  30. }
  31.  
  32. Expand Methods

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

  1. entrypoint
  2. .maxstack
  3. .locals init (
  4. [] int32 number,
  5. [] class [mscorlib]System.Collections.Generic.IEnumerator`<int32> CS$$)
  6. L_0000: ldc.i4.
  7. L_0001: call class [mscorlib]System.Collections.Generic.IEnumerable`<int32> ConsoleApplication1.Program::GetNumbers(int32)
  8. L_0006: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`<!> [mscorlib]System.Collections.Generic.IEnumerable`<int32>::GetEnumerator()
  9. L_000b: stloc.
  10. L_000c: br.s L_0026
  11. L_000e: ldloc.
  12. L_000f: callvirt instance ! [mscorlib]System.Collections.Generic.IEnumerator`<int32>::get_Current()
  13. L_0014: stloc.
  14. L_0015: ldstr "\u7ed3\u679c\uff1a{0}"
  15. L_001a: ldloca.s number
  16. L_001c: call instance string [mscorlib]System.Int32::ToString()
  17. L_0021: call void [mscorlib]System.Console::WriteLine(string, object)
  18. L_0026: ldloc.
  19. L_0027: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
  20. L_002c: brtrue.s L_000e
  21. L_002e: leave.s L_003a
  22. L_0030: ldloc.
  23. L_0031: brfalse.s L_0039
  24. L_0033: ldloc.
  25. L_0034: callvirt instance void [mscorlib]System.IDisposable::Dispose()
  26. L_0039: endfinally
  27. L_003a: ret
  28. .try L_000c to L_0030 finally handler L_0030 to L_003a

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

  1. private bool MoveNext()
  2. {
  3. try
  4. {
  5. switch (this.<>1__state)
  6. {
  7. case :
  8. this.<>1__state = -;
  9. this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject");
  10. this.<>1__state = ;
  11. this.<>7__wrap5 = Enumerable.Range(, this.count).GetEnumerator();
  12. this.<>1__state = ;
  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 = ;
  18. if (this.<number>5__2 == )
  19. {
  20. throw new Exception("异常。");
  21. }
  22. if (this.<number>5__2 == )
  23. {
  24. goto Label_00D0;
  25. }
  26. this.<>2__current = this.<number>5__2 * ;
  27. this.<>1__state = ;
  28. return true;
  29. Label_00C7:
  30. this.<>1__state = ;
  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 :
  43. goto Label_00C7;
  44. }
  45. return false;
  46. }
  47. fault
  48. {
  49. this.System.IDisposable.Dispose();
  50. }
  51. }

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

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

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

  1. .entrypoint
  2. .maxstack
  3. .locals init (
  4. [] class ConsoleApplication1.DisposableObject parentDisposableObject)
  5. L_0000: ldstr "MainDisposableObject"
  6. L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
  7. L_000a: stloc.
  8. L_000b: ldstr "\u6267\u884c..."
  9. L_0010: call void [mscorlib]System.Console::WriteLine(string)
  10. L_0015: leave.s L_0021
  11. L_0017: ldloc.
  12. L_0018: brfalse.s L_0020
  13. L_001a: ldloc.
  14. L_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
  15. L_0020: endfinally
  16. L_0021: ret
  17. .try L_000b to L_0017 finally handler L_0017 to L_0021

再换一种C#写法试试:

  1. DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject");
  2. try
  3. {
  4. Console.WriteLine("执行...");
  5. //throw new Exception("异常。");
  6. }
  7. finally
  8. {
  9. parentDisposableObject.Dispose();
  10. }

对应的MSIL代码:

  1. .entrypoint
  2. .maxstack
  3. .locals init (
  4. [] class ConsoleApplication1.DisposableObject parentDisposableObject)
  5. L_0000: ldstr "MainDisposableObject"
  6. L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
  7. L_000a: stloc.
  8. L_000b: ldstr "\u6267\u884c..."
  9. L_0010: call void [mscorlib]System.Console::WriteLine(string)
  10. L_0015: leave.s L_001e
  11. L_0017: ldloc.
  12. L_0018: callvirt instance void ConsoleApplication1.DisposableObject::Dispose()
  13. L_001d: endfinally
  14. L_001e: ret
  15. .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 :
  8. this.<>1__state = -;
  9. this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject");
  10. this.<>1__state = ;
  11. this.<>7__wrap5 = Enumerable.Range(, this.count).GetEnumerator();
  12. this.<>1__state = ;
  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 = ;
  18. if (this.<number>5__2 == )
  19. {
  20. throw new Exception("异常。");
  21. }
  22. if (this.<number>5__2 == )
  23. {
  24. goto Label_00D0;
  25. }
  26. this.<>2__current = this.<number>5__2 * ;
  27. this.<>1__state = ;
  28. return true;
  29. Label_00C7:
  30. this.<>1__state = ;
  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 :
  43. goto Label_00C7;
  44. }
  45. return false;
  46. }
  47. fault
  48. {
  49. this.System.IDisposable.Dispose();
  50. }
  51. }
  1. public DisposableObject <parentDisposableObject>5__1;
  2.  
  3. public DisposableObject <childDisposableObject>5__3;
  4.  
  5. private void <>m__Finally4()
  6. {
  7. this.<>1__state = -;
  8. if (this.<parentDisposableObject>5__1 != null)
  9. {
  10. this.<parentDisposableObject>5__1.Dispose();
  11. }
  12. }
  13.  
  14. private void <>m__Finally7()
  15. {
  16. this.<>1__state = ;
  17. if (this.<childDisposableObject>5__3 != null)
  18. {
  19. this.<childDisposableObject>5__3.Dispose();
  20. }
  21. }
  22.  
  23. void IDisposable.Dispose()
  24. {
  25. switch (this.<>1__state)
  26. {
  27. case :
  28. case :
  29. case :
  30. case :
  31. try
  32. {
  33. switch (this.<>1__state)
  34. {
  35. case :
  36. case :
  37. case :
  38. try
  39. {
  40. switch (this.<>1__state)
  41. {
  42. case :
  43. case :
  44. try
  45. {
  46. }
  47. finally
  48. {
  49. this.<>m__Finally7();
  50. }
  51. break;
  52. }
  53. }
  54. finally
  55. {
  56. this.<>m__Finally6();
  57. }
  58. break;
  59. }
  60. }
  61. finally
  62. {
  63. this.<>m__Finally4();
  64. }
  65. break;
  66.  
  67. default:
  68. return;
  69. }
  70. }

C#中的using和yield return混合使用的更多相关文章

  1. C#中,什么时候用yield return

    yield关键字用于遍历循环中,yield return用于返回IEnumerable<T>,yield break用于终止循环遍历. 有这样的一个int类型的集合: static Lis ...

  2. using和yield return

    C#中的using和yield return混合使用 最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IE ...

  3. 可惜Java中没有yield return

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

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

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

  5. C#中yield return用法分析

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

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

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

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

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

  8. C#中的yield return用法演示源码

    下边代码段是关于C#中的yield return用法演示的代码. using System;using System.Collections;using System.Collections.Gene ...

  9. Unity中yield return null和yield return WaitForEndOfFrame的区别

    2017/07/04修改 - 对WaitForEndOfFrame的LateUpdate时序进行说明. 测试结论: 1.如果只是等待下一帧执行,用yield return null即可.调用顺序在Up ...

随机推荐

  1. mobx源码解读4

    这节介绍一下mobx的变动因子的稳定性. mobx整个系统是由ObservableValue, ComputedValue, Reaction这三个东西构建的 ObservableValue 是最小的 ...

  2. DSP中的段

    虽然,C语言是一种相对高效的高级语言,并且TI提供的C编译器还结合硬件特点支持三级优化功能,但生成的汇编代码效率仍可能会不尽人意.如作者预使用环型缓冲区管理功能,这就要求该缓冲区应被定位到相对特定的位 ...

  3. Button模板,样式

    一.button控件上的模板 <Button Content="Button" Height="25" HorizontalAlignment=" ...

  4. [Docker] docker 基础学习笔记2(共6篇)

    febootstrap 是一个自制image的一个第三方的工具,好像他们现在都要用这个.   安装还是挺方便的. yum -y install febootstrap   febootstrap -i ...

  5. Java实现文件压缩与解压

    Java实现ZIP的解压与压缩功能基本都是使用了Java的多肽和递归技术,可以对单个文件和任意级联文件夹进行压缩和解压,对于一些初学者来说是个很不错的实例.(转载自http://www.puiedu. ...

  6. Yii;CodeIgniter;thinkphp学习

    http://www.ibm.com/developerworks/cn/opensource/os-cn-yii/ http://codeigniter.org.cn/ http://baike.b ...

  7. 修改tomcat的端口

  8. SQL 分页

    sql = "SELECT TOP 10000 * " + " FROM(SELECT ROW_NUMBER() OVER(ORDER BY DataArticleID) ...

  9. Winfrom实现圆角设计

    主要代码 public partial class Form1 : Form    {        public Form1()        {            InitializeComp ...

  10. iOS 定位功能的实现

    1.导入框架 Xcode中添加"CoreLocation.framework" 2.导入主头文件 #import <CoreLocation/CoreLocation.h&g ...