using和yield return

时间:2023-03-09 21:27:23
using和yield return

C#中的using和yield return混合使用

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

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

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

using和yield return
        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());
}
}
}
}
}
using和yield return

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

using和yield return
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);
}
}
using和yield return

这里调用下:

using和yield return
        static void Main(string[] args)
{
foreach (int number in GetNumbers(5))
{
Console.WriteLine("结果 {0}", number.ToString());
}
}
using和yield return

看看运行结果:

using和yield return

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

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

using和yield return

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

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

using和yield return
        private static IEnumerable<int> GetNumbers(int[] numbers)
{
foreach (int number in numbers)
{
yield return number*10;
}
}
using和yield return

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

using和yield return
private static IEnumerable<int> GetNumbers(int[] numbers)
{
<GetNumbers>d__0 d__ = new <GetNumbers>d__0(-2);
d__.<>3__numbers = numbers;
return d__;
}
using和yield return

这里的<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:

using和yield return
    .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
using和yield return

再换一种C#写法试试:

using和yield return
            DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject");
try
{
Console.WriteLine("执行...");
//throw new Exception("异常。");
}
finally
{
parentDisposableObject.Dispose();
}
using和yield return

对应的MSIL代码:

using和yield return
    .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
using和yield return

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

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

using和yield return
 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
using和yield return
using和yield return
 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 }
using和yield return
分类: .NET开发C#语法