C#中的using和yield return混合使用.docx
《C#中的using和yield return混合使用.docx》由会员分享,可在线阅读,更多相关《C#中的using和yield return混合使用.docx(16页珍藏版)》请在冰豆网上搜索。
C#中的using和yieldreturn混合使用
最近写代码为了为了省事儿用了几个yieldreturn,因为我不想New一个List或者T[]对象再往里放元素,就直接返回IEnumerable了。
我的代码里还有很多需要Dispose的对象,所以又用了几个using。
写着写着我有点心虚了——这样混合使用靠谱吗?
今天我花时间研究一下,并在这里作个笔记,跟大家分享。
笔者水平有限,有哪些理解错误或做的不到位的地方,还请各位专家点拨。
这是我写的方法,循环外面一个using,整个方法里代码执行后释放一个对象。
循环里面又一个using,每次循环yieldreturn后要释放一个对象。
那是不是任何情况下这些[被创建了的需要释放的]DisposableObject对象最后都会被释放呢?
privatestaticIEnumerableGetNumbers(intcount)
{
using(DisposableObjectparentDisposableObject=newDisposableObject("ParentDisposableObject"))
{
foreach(intnumberinEnumerable.Range(1,count))
{
using(DisposableObjectchildDisposableObject=newDisposableObject(string.Format("ChildDisposableObject{0}",number)))
{
//if(number==4)
//{
//thrownewException("异常。
");
//}
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("CreateObject{0}",_value);
}
publicvoidDispose()
{
Console.WriteLine("DisposableObject{0}",_value);
}
}
这里调用下:
staticvoidMain(string[]args)
{
foreach(intnumberinGetNumbers(5))
{
Console.WriteLine("结果{0}",number.ToString());
}
}
看看运行结果:
我们可以看到:
1、循环外面的对象和循环里面的DisposableObject对象都被释放了,这个让我很高兴,要的就是这个效果;2,如果yieldreturn后面还有代码,[yield]return后还会继续执行;3,if-else有作用,不满足条件可以不把该项作为结果返回,不想执行某段代码可以放{}里。
这个运行的结果我很满意,就是我想要的!
下面我把抛异常的代码注释去掉,看看循环内抛出的异常后能否正常释放对象。
结果很完美,担忧是多余的,该释放的DisposableObject对象都被释放了!
那么我们简单研究下yieldreturn吧,我写了下面最简单的代码:
privatestaticIEnumerableGetNumbers(int[]numbers)
{
foreach(intnumberinnumbers)
{
yieldreturnnumber*10;
}
}
把项目编译再反编译成C#2.0,发现代码变成了这个样子:
privatestaticIEnumerableGetNumbers(int[]numbers)
{
d__0d__=newd__0(-2);
d__.<>3__numbers=numbers;
returnd__;
}
这里的d__0是个自动生成的类(看来这是高热量的语法糖,吃的是少了,程序集却发胖了!
),它实现了IEnumerable,IEnumerator等接口,而上面方法其实就是返回了一个封装了迭代器块代码的计数对象而已,如果您仅仅调用了一下上面这个方法,它可能不会执行循环中的代码,除非触发了返回值的MoveNext方法,这就是传说中的延迟求值吧!
[CompilerGenerated]
privatesealedclassd__0:
IEnumerable,IEnumerable,IEnumerator,IEnumerator,IDisposable
{
//Fields
privateint<>1__state;
privateint<>2__current;
publicint[]<>3__numbers;
publicint[]<>7__wrap3;
publicint<>7__wrap4;
privateint<>l__initialThreadId;
publicint5__1;
publicint[]numbers;
//Methods
[DebuggerHidden]
publicd__0(int<>1__state);
privatevoid<>m__Finally2();
privateboolMoveNext();
[DebuggerHidden]
IEnumeratorIEnumerable.GetEnumerator();
[DebuggerHidden]
IEnumeratorIEnumerable.GetEnumerator();
[DebuggerHidden]
voidIEnumerator.Reset();
voidIDisposable.Dispose();
//Properties
intIEnumerator.Current{[DebuggerHidden]get;}
objectIEnumerator.Current{[DebuggerHidden]get;}
}
ExpandMethods
ViewCode
通过MSIL查看上面的foreach循环会调用MoveNext方法。
entrypoint
.maxstack2
.localsinit(
[0]int32number,
[1]class[mscorlib]System.Collections.Generic.IEnumerator`1CS$5$0000)
L_0000:
ldc.i4.5
L_0001:
callclass[mscorlib]System.Collections.Generic.IEnumerable`1ConsoleApplication1.Program:
:
GetNumbers(int32)
L_0006:
callvirtinstanceclass[mscorlib]System.Collections.Generic.IEnumerator`1
0>[mscorlib]System.Collections.Generic.IEnumerable`1:
:
GetEnumerator()
L_000b:
stloc.1
L_000c:
br.sL_0026
L_000e:
ldloc.1
L_000f:
callvirtinstance!
0[mscorlib]System.Collections.Generic.IEnumerator`1:
:
get_Current()
L_0014:
stloc.0
L_0015:
ldstr"\u7ed3\u679c\uff1a{0}"
L_001a:
ldloca.snumber
L_001c:
callinstancestring[mscorlib]System.Int32:
:
ToString()
L_0021:
callvoid[mscorlib]System.Console:
:
WriteLine(string,object)
L_0026:
ldloc.1
L_0027:
callvirtinstancebool[mscorlib]System.Collections.IEnumerator:
:
MoveNext()
L_002c:
brtrue.sL_000e
L_002e:
leave.sL_003a
L_0030:
ldloc.1
L_0031:
brfalse.sL_0039
L_0033:
ldloc.1
L_0034:
callvirtinstancevoid[mscorlib]System.IDisposable:
:
Dispose()
L_0039:
endfinally
L_003a:
ret
.tryL_000ctoL_0030finallyhandlerL_0030toL_003a
ViewCode
而循环里面的执行内容都在MoveNext方法里。
privateboolMoveNext()
{
try
{
switch(this.<>1__state)
{
case0:
this.<>1__state=-1;
this.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.5__2=this.<>7__wrap5.Current;
this.5__3=newDisposableObject(string.Format("ChildDisposableObject{0}",this.5__2));
this.<>1__state=3;
if(this.5__2==4)
{
thrownewException("异常。
");
}
if(this.5__2==2)
{
gotoLabel_00D0;
}
this.<>2__current=this.5__2*10;
this.<>1__state=4;
returntrue;
Label_00C7:
this.<>1__state=3;
gotoLabel_00E8;
Label_00D0:
Console.WriteLine("循环{0}:
else内代码执行了",this.5__2.ToString());
Label_00E8:
Console.WriteLine("循环{0}:
else下面的代码执行了",this.5__2.ToString());
this.<>m__Finally7();
}
this.<>m__Finally6();
this.<>m__Finally4();
break;
case4:
gotoLabel_00C7;
}
returnfalse;
}
fault
{
this.System.IDisposable.Dispose();
}
}
ViewCode
接着再看下using,也来个最简单的。
using(DisposableObjectparentDisposableObject=newDisposableObject("MainDisposableObject"))
{
Console.WriteLine("执行...");
//thrownewException("异常。
");
}
然后我们看一下对应的MSIL:
.entrypoint
.maxstack1
.localsinit(
[0]classConsoleApplication1.DisposableObjectparentDisposableObject)
L_0000:
ldstr"MainDisposableObject"
L_0005:
newobjinstancevoidConsoleApplication1.DisposableObject:
:
.ctor(string)
L_000a:
stloc.0
L_000b:
ldstr"\u6267\u884c..."
L_0010:
callvoid[mscorlib]System.Console:
:
WriteLine(string)
L_0015:
leave.sL_0021
L_0017:
ldloc.0
L_0018:
brfalse.sL_0020
L_001a:
ldloc.0
L_001b:
callvirtinstancevoid[mscorlib]System.IDisposable:
:
Dispose()
L_0020:
endfinally
L_0021:
ret
.tryL_000btoL_0017finallyhandlerL_0017toL_0021
再换一种C#写法试试:
DisposableObjectparentDisposableObject=newDisposableObject("MainDisposableObject");
try
{
Console.WriteLine("执行...");
//thrownewException("异常。
");
}
finally
{
parentDisposableObject.Dispose();
}
对应的MSIL代码:
.entrypoint
.maxstack1
.localsinit(
[0]classConsoleApplication1.DisposableObjectparentDisposableObject)
L_0000:
ldstr"MainDisposableObject"
L_0005:
newobjinstancevoidConsoleApplication1.DisposableObject:
:
.ctor(string)
L_000a:
stloc.0
L_000b:
ldstr"\u6267\u884c..."
L_0010:
callvoid[mscorlib]System.Console:
:
WriteLine(string)
L_0015:
leave.sL_001e
L_0017:
ldloc.0
L_0018:
callvirtinstancevoidConsoleApplication1.DisposableObject:
:
Dispose()
L_001d:
endfinally
L_001e:
ret
.tryL_000btoL_0017finallyhandlerL_0017toL_001e
看看两段MSIL多像啊,特别是最后一句!
最后我们看看yieldreturn和using混合使用时,自动生成的d__0类是如何保证需要释放资源的DisposableObject对象被释放的,看后我不禁感慨:
C#的编译器真是鬼斧神工啊!
1privateboolMoveNext()
2{
3try
4{
5switch(this.<>1__state)
6{
7case0:
8this.<>1__state=-1;
9this.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.5__2=this.<>7__wrap5.Current;
16this.5__3=newDisposableObject(string.Format("ChildDisposableObject{0}",this.5__2));
17this.<>1__state=3;
18if(this.5__2==4)
19{
20thrownewException("异常。
");
21}
22if(this.5__2==2)
23{
24gotoLabel_00D0;
25}
26this.<>2__current=this.5__2*10;
27this.<>1__state=4;
28returntrue;
29Label_00C7:
30this.<>1__state=3;
31gotoLabel_00E8;
32Label_00D0:
33Console.WriteLine("循环{0}:
else内代码执行了",this.5__2.ToString());
34Label_00E8:
35Console.WriteLine("循环{0}:
else下面的代码执行了",this.5__2.ToString());
36this.<>m__Finally7();
37}
38this.<>m__Finally6();
39this.<>m__Finally4();
40break;
41
42case4:
43gotoLabel_00C7;
44}
45returnfalse;
46}
47fault
48{
49this.System.IDisposable.Dispose();
50}
51}
52
53
54
55
1publicDisposableObject5__1;
2
3
4publicDisposableObject5__3;
5
6
7privatevoid<>m__Finally4()
8{
9this.<>1__state=-1;
10if(this.5__1!
=null)
11{
12this.5__1.Dispose();
13}
14}
15
16privatevoid<>m__Finally7()
17{
18this.<>1__state=2;
19if(this.5__3!
=null)
20{
21this.5__3.Dispose();
22}
23}
24
25voidIDisposable.Dispose()
26{
27switch(this.<>1__state)
28{
29case1:
30case2:
31case3:
32case4:
33try
34{
35switch(this.<>1__state)
36{
37case2:
38case3:
39case4:
40try
41{
42switch(this.<>1__state)
43{
44case3:
45case4:
46try
47{
48}
49finally
50{
51this.<>m__Finally7();
52}
53break;
54}
55}
56finally
57{
58this.<>m__Finally6();
59}
60break;
61}
62}
63finally
64{
65this.<>m__Finally4();
66}
67break;
68
69default:
70return;
71}
72}