C#中的using和yieldreturn混合使用

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

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

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

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

PRivatestaticIEnumerable<int> GetNumbers(intcount)

{using(DisposableObject parentDisposableObject =newDisposableObject("ParentDisposableObject"))

{foreach(intnumberinEnumerable.Range(1, count))

{using(DisposableObject childDisposableObject =newDisposableObject(string.Format("ChildDisposableObject{0}", number)))

{//if (number == 4)//{//throw new Exception("异常。");//}if(number !=2)

{yieldreturnnumber *10;

}else{

Console.WriteLine("循环{0} else 代码执行了", number.ToString());

}

Console.WriteLine("循环{0}else下面的代码执行了", number.ToString());

}

}

}

}

}

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

classDisposableObject : IDisposable

{privatestring_value;publicDisposableObject(stringvalue)

{

_value=value;

Console.WriteLine("Create Object {0}", _value);

}publicvoidDispose()

{

Console.WriteLine("Disposable Object {0}", _value);

}

}

这里调用下:

staticvoidMain(string[] args)

{foreach(intnumberinGetNumbers(5))

{

Console.WriteLine("结果{0}", number.ToString());

}

}

看看运行结果:

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

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

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

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

privatestaticIEnumerable<int> GetNumbers(int[] numbers)

{foreach(intnumberinnumbers)

{yieldreturnnumber*10;

}

}

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

privatestaticIEnumerable<int> GetNumbers(int[] numbers)

{<GetNumbers>d__0 d__ =new<GetNumbers>d__0(-2);d__.<>3__numbers =numbers;returnd__;

}

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

[CompilerGenerated]privatesealedclass<GetNumbers>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable

{//Fieldsprivateint<>1__state;privateint<>2__current;publicint[] <>3__numbers;publicint[] <>7__wrap3;publicint<>7__wrap4;privateint<>l__initialThreadId;publicint<number>5__1;publicint[] numbers;//Methods[DebuggerHidden]public<GetNumbers>d__0(int<>1__state);privatevoid<>m__Finally2();privateboolMoveNext();

[DebuggerHidden]

IEnumerator<int> IEnumerable<int>.GetEnumerator();

[DebuggerHidden]

IEnumerator IEnumerable.GetEnumerator();

[DebuggerHidden]voidIEnumerator.Reset();voidIDisposable.Dispose();//PropertiesintIEnumerator<int>.Current { [DebuggerHidden]get; }objectIEnumerator.Current { [DebuggerHidden]get; }

}

Expand Methods

View Code

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

entrypoint

.maxstack2.locals init (

[0] int32 number,

[1]class[mscorlib]System.Collections.Generic.IEnumerator`1<int32> CS$5$0000)

L_0000: ldc.i4.5L_0001: callclass[mscorlib]System.Collections.Generic.IEnumerable`1<int32>Consoleapplication1.Program::GetNumbers(int32)

L_0006: callvirt instanceclass[mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()

L_000b: stloc.1L_000c: br.s L_0026

L_000e: ldloc.1L_000f: callvirt instance!0[mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()

L_0014: stloc.0L_0015: ldstr"\u7ed3\u679c\uff1a{0}"L_001a: ldloca.s number

L_001c: call instancestring[mscorlib]System.Int32::ToString()

L_0021: callvoid[mscorlib]System.Console::WriteLine(string,object)

L_0026: ldloc.1L_0027: callvirt instancebool[mscorlib]System.Collections.IEnumerator::MoveNext()

L_002c: brtrue.s L_000e

L_002e: leave.s L_003a

L_0030: ldloc.1L_0031: brfalse.s L_0039

L_0033: ldloc.1L_0034: callvirt instancevoid[mscorlib]System.IDisposable::Dispose()

L_0039: endfinally

L_003a: ret

.tryL_000c to L_0030finallyhandler L_0030 to L_003a

View Code

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

privateboolMoveNext()

{try{switch(this.<>1__state)

{case0:this.<>1__state = -1;this.<parentDisposableObject>5__1 =newDisposableObject("ParentDisposableObject");this.<>1__state =1;this.<>7__wrap5 = Enumerable.Range(1,this.count).GetEnumerator();this.<>1__state =2;while(this.<>7__wrap5.MoveNext())

{this.<number>5__2 =this.<>7__wrap5.Current;this.<childDisposableObject>5__3 =newDisposableObject(string.Format("ChildDisposableObject{0}",this.<number>5__2));this.<>1__state =3;if(this.<number>5__2 ==4)

{thrownewException("异常。");

}if(this.<number>5__2 ==2)

{gotoLabel_00D0;

}this.<>2__current =this.<number>5__2 *10;this.<>1__state =4;returntrue;

Label_00C7:this.<>1__state =3;gotoLabel_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;case4:gotoLabel_00C7;

}returnfalse;

}

fault

{this.System.IDisposable.Dispose();

}

}

View Code

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

using(DisposableObject parentDisposableObject =newDisposableObject("MainDisposableObject"))

{

Console.WriteLine("执行...");//throw new Exception("异常。");}

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

.entrypoint

.maxstack1.locals init (

[0]classConsoleApplication1.DisposableObject parentDisposableObject)

L_0000: ldstr"MainDisposableObject"L_0005: newobj instancevoidConsoleApplication1.DisposableObject::.ctor(string)

L_000a: stloc.0L_000b: ldstr"\u6267\u884c..."L_0010: callvoid[mscorlib]System.Console::WriteLine(string)

L_0015: leave.s L_0021

L_0017: ldloc.0L_0018: brfalse.s L_0020

L_001a: ldloc.0L_001b: callvirt instancevoid[mscorlib]System.IDisposable::Dispose()

L_0020: endfinally

L_0021: ret.tryL_000b to L_0017finallyhandler L_0017 to L_0021

再换一种C#写法试试:

DisposableObject parentDisposableObject =newDisposableObject("MainDisposableObject");try{

Console.WriteLine("执行...");//throw new Exception("异常。");}finally{

parentDisposableObject.Dispose();

}

对应的MSIL代码:

.entrypoint

.maxstack1.locals init (

[0]classConsoleApplication1.DisposableObject parentDisposableObject)

L_0000: ldstr"MainDisposableObject"L_0005: newobj instancevoidConsoleApplication1.DisposableObject::.ctor(string)

L_000a: stloc.0L_000b: ldstr"\u6267\u884c..."L_0010: callvoid[mscorlib]System.Console::WriteLine(string)

L_0015: leave.s L_001e

L_0017: ldloc.0L_0018: callvirt instancevoidConsoleApplication1.DisposableObject::Dispose()

L_001d: endfinally

L_001e: ret.tryL_000b to L_0017finallyhandler L_0017 to L_001e

看看两段MSIL多像啊,特别是最后一句!

最后我们看看yield return 和 using混合使用时,自动生成的<GetNumbers>d__0类是如何保证需要释放资源的DisposableObject对象被释放的,看后我不禁感慨:C#的编译器真是鬼斧神工啊!

1privateboolMoveNext()2{3try4{5switch(this.<>1__state)6{7case0:8this.<>1__state = -1;9this.<parentDisposableObject>5__1 =newDisposableObject("ParentDisposableObject");10this.<>1__state =1;11this.<>7__wrap5 = Enumerable.Range(1,this.count).GetEnumerator();12this.<>1__state =2;13while(this.<>7__wrap5.MoveNext())14{15this.<number>5__2 =this.<>7__wrap5.Current;16this.<childDisposableObject>5__3 =newDisposableObject(string.Format("ChildDisposableObject{0}",this.<number>5__2));17this.<>1__state =3;18if(this.<number>5__2 ==4)19{20thrownewException("异常。");21}22if(this.<number>5__2 ==2)23{24gotoLabel_00D0;25}26this.<>2__current =this.<number>5__2 *10;27this.<>1__state =4;28returntrue;29Label_00C7:30this.<>1__state =3;31gotoLabel_00E8;32Label_00D0:33Console.WriteLine("循环{0}:else 内代码执行了",this.<number>5__2.ToString());34Label_00E8:35Console.WriteLine("循环{0}:else下面的代码执行了",this.<number>5__2.ToString());36this.<>m__Finally7();37}38this.<>m__Finally6();39this.<>m__Finally4();40

break;4142case4:43gotoLabel_00C7;44}45returnfalse;46}47fault48{49this.System.IDisposable.Dispose();50}51}52535455

1publicDisposableObject <parentDisposableObject>5__1;234publicDisposableObject <childDisposableObject>5__3;567privatevoid<>m__Finally4()8{9this.<>1__state = -1;10if(this.<parentDisposableObject>5__1 !=null)11{12this.<parentDisposableObject>5__1.Dispose();13}14}1516privatevoid<>m__Finally7()17{18this.<>1__state =2;19if(this.<childDisposableObject>5__3 !=null)20{21this.<childDisposableObject>5__3.Dispose();22}23}2425voidIDisposable.Dispose()26{27switch(this.<>1__state)28{29case1:30case2:31case3:32case4:33try34{35switch(this.<>1__state)36{37case2:38case3:39case4:40try41{42switch(this.<>1__state)43{44case3:45case4:46try47{48}49finally50{51this.<>m__Finally7();52}53

break;54}55}56finally57{58this.<>m__Finally6();59}60

break;61}62}63finally64{65this.<>m__Finally4();66}67

break;6869default:70return;71}72}

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航