1、C#中的using和yield return混合使用 最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List或者T对象再往里放元素,就直接返回IEnumerable了。我的代码里还有很多需要Dispose的对象,所以又用了几个using。写着写着我有点心虚了这样混合使用靠谱吗? 今天我花时间研究一下,并在这里作个笔记,跟大家分享。笔者水平有限,有哪些理解错误或做的不到位的地方,还请各位专家点拨。 这是我写的方法,循环外面一个using,整个方法里代码执行后释放一个对象。循环里面又一个using, 每次循环yield return后要释放一个对象。那是不是任何情况
2、下这些被创建了的需要释放的DisposableObject对象最后都会被释放呢? private static IEnumerable GetNumbers(int count) using (DisposableObject parentDisposableObject = new DisposableObject(ParentDisposableObject) foreach (int number in Enumerable.Range(1, count) using (DisposableObject childDisposableObject = new DisposableObje
3、ct(string.Format(ChildDisposableObject0, number) /if (number = 4) / / throw new Exception(异常。); / if (number != 2) yield return number * 10; else Console.WriteLine( 循环0 else 代码执行了, number.ToString(); Console.WriteLine( 循环0else下面的代码执行了, number.ToString(); 需要释放资源的类定义如下,创建对象和释放时都有输出。class DisposableObj
4、ect : 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
5、(结果 0, number.ToString(); 看看运行结果: 我们可以看到:1、循环外面的对象和循环里面的DisposableObject对象都被释放了,这个让我很高兴,要的就是这个效果;2,如果yield return后面还有代码,yield return后还会继续执行;3,if-else有作用,不满足条件可以不把该项作为结果返回,不想执行某段代码可以放里。这个运行的结果我很满意,就是我想要的! 下面我把抛异常的代码注释去掉,看看循环内抛出的异常后能否正常释放对象。结果很完美,担忧是多余的,该释放的DisposableObject对象都被释放了! 那么我们简单研究下yield retu
6、rn吧,我写了下面最简单的代码: private static IEnumerable GetNumbers(int numbers) foreach (int number in numbers) yield return number*10; 把项目编译再反编译成C#2.0,发现代码变成了这个样子:private static IEnumerable GetNumbers(int numbers) d_0 d_ = new d_0(-2); d_.3_numbers = numbers; return d_; 这里的d_0是个自动生成的类(看来这是高热量的语法糖,吃的是少了,程序集却发胖了
7、!),它实现了IEnumerable,IEnumerator等接口,而上面方法其实就是返回了一个封装了迭代器块代码的计数对象而已,如果您仅仅调用了一下上面这个方法,它可能不会执行循环中的代码,除非触发了返回值的MoveNext方法,这就是传说中的延迟求值吧!CompilerGeneratedprivate sealed class d_0 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable / Fields private int 1_state; private int 2_current; public int
8、 3_numbers; public int 7_wrap3; public int 7_wrap4; private int l_initialThreadId; public int 5_1; public int numbers; / Methods DebuggerHidden public d_0(int 1_state); private void m_Finally2(); private bool MoveNext(); DebuggerHidden IEnumerator IEnumerable.GetEnumerator(); DebuggerHidden IEnumera
9、tor IEnumerable.GetEnumerator(); DebuggerHidden void IEnumerator.Reset(); void IDisposable.Dispose(); / Properties int IEnumerator.Current DebuggerHidden get; object IEnumerator.Current DebuggerHidden get; Expand Methods View Code 通过MSIL查看上面的foreach循环会调用MoveNext方法。entrypoint .maxstack 2 .locals init
10、 ( 0 int32 number, 1 class mscorlibSystem.Collections.Generic.IEnumerator1 CS$5$0000) L_0000: ldc.i4.5 L_0001: call class mscorlibSystem.Collections.Generic.IEnumerable1 ConsoleApplication1.Program:GetNumbers(int32) L_0006: callvirt instance class mscorlibSystem.Collections.Generic.IEnumerator1 msco
11、rlibSystem.Collections.Generic.IEnumerable1:GetEnumerator() L_000b: stloc.1 L_000c: br.s L_0026 L_000e: ldloc.1 L_000f: callvirt instance !0 mscorlibSystem.Collections.Generic.IEnumerator1:get_Current() L_0014: stloc.0 L_0015: ldstr u7ed3u679cuff1a0 L_001a: ldloca.s number L_001c: call instance stri
12、ng mscorlibSystem.Int32:ToString() L_0021: call void mscorlibSystem.Console:WriteLine(string, object) L_0026: ldloc.1 L_0027: callvirt instance bool mscorlibSystem.Collections.IEnumerator:MoveNext() L_002c: brtrue.s L_000e L_002e: leave.s L_003a L_0030: ldloc.1 L_0031: brfalse.s L_0039 L_0033: ldloc
13、.1 L_0034: callvirt instance void mscorlibSystem.IDisposable:Dispose() L_0039: endfinally L_003a: ret .try L_000c to L_0030 finally handler L_0030 to L_003aView Code 而循环里面的执行内容都在MoveNext方法里。private bool MoveNext() try switch (this.1_state) case 0: this.1_state = -1; this.5_1 = new DisposableObject(P
14、arentDisposableObject); this.1_state = 1; this.7_wrap5 = Enumerable.Range(1, this.count).GetEnumerator(); this.1_state = 2; while (this.7_wrap5.MoveNext() this.5_2 = this.7_wrap5.Current; this.5_3 = new DisposableObject(string.Format(ChildDisposableObject0, this.5_2); this.1_state = 3; if (this.5_2
15、= 4) throw new Exception(异常。); if (this.5_2 = 2) goto Label_00D0; this.2_current = this.5_2 * 10; this.1_state = 4; return true; Label_00C7: this.1_state = 3; goto Label_00E8; Label_00D0: Console.WriteLine(循环0:else 内代码执行了 , this.5_2.ToString(); Label_00E8: Console.WriteLine(循环0:else下面的代码执行了 , this.5
16、_2.ToString(); this.m_Finally7(); this.m_Finally6(); this.m_Finally4(); break; case 4: goto Label_00C7; return false; fault this.System.IDisposable.Dispose(); View Code 接着再看下using,也来个最简单的。 using (DisposableObject parentDisposableObject = new DisposableObject(MainDisposableObject) Console.WriteLine(执
17、行.); /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
18、 u6267u884c. L_0010: call void mscorlibSystem.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 mscorlibSystem.IDisposable:Dispose() L_0020: endfinally L_0021: ret .try L_000b to L_0017 finally handler L_0017 to L
19、_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 pare
20、ntDisposableObject) L_0000: ldstr MainDisposableObject L_0005: newobj instance void ConsoleApplication1.DisposableObject:.ctor(string) L_000a: stloc.0 L_000b: ldstr u6267u884c. L_0010: call void mscorlibSystem.Console:WriteLine(string) L_0015: leave.s L_001e L_0017: ldloc.0 L_0018: callvirt instance
21、 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混合使用时,自动生成的d_0类是如何保证需要释放资源的DisposableObject对象被释放的,看后我不禁感慨:C#的编译器真是鬼斧神工啊! 1 private bool MoveNext() 2 3 try 4 5 swi
22、tch (this.1_state) 6 7 case 0: 8 this.1_state = -1; 9 this.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.5_2 = this.7_wrap5.Current;16 this.5_3 =
23、 new DisposableObject(string.Format(ChildDisposableObject0, this.5_2);17 this.1_state = 3;18 if (this.5_2 = 4)19 20 throw new Exception(异常。);21 22 if (this.5_2 = 2)23 24 goto Label_00D0;25 26 this.2_current = this.5_2 * 10;27 this.1_state = 4;28 return true;29 Label_00C7:30 this.1_state = 3;31 goto
24、Label_00E8;32 Label_00D0:33 Console.WriteLine(循环0:else 内代码执行了 , this.5_2.ToString();34 Label_00E8:35 Console.WriteLine(循环0:else下面的代码执行了 , this.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 fault48 49
25、 this.System.IDisposable.Dispose();50 51 52 53 54 55 1 public DisposableObject 5_1; 2 3 4 public DisposableObject 5_3; 5 6 7 private void m_Finally4() 8 9 this.1_state = -1;10 if (this.5_1 != null)11 12 this.5_1.Dispose();13 14 15 16 private void m_Finally7()17 18 this.1_state = 2;19 if (this.5_3 !=
26、 null)20 21 this.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 try34 35 switch (this.1_state)36 37 case 2:38 case 3:39 case 4:40 try41 42 switch (this.1_state)43 44 case 3:45 case 4:46 try47 48 49 finally50 51 this.m_Finally7();52 53 break;54 55 56 finally57 58 this.m_Finally6();59 60 break;61 62 63 finally64 65 this.m_Finally4();66 67 break;68 69 default:70 return;71 72
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1