博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
编写高质量代码改善C#程序的157个建议:第17个建议之多数情况下使用foreach进行循环遍历...
阅读量:6095 次
发布时间:2019-06-20

本文共 13047 字,大约阅读时间需要 43 分钟。

      今天是我看《编写高质量代码:改善C#程序的157个建议》第二遍的时候了,看完这本书的确是受益匪浅,学到了很多东西,也明白了很多道理。

     里面的代码我每个都调试了一遍,有时候是有些出入的,可能是作者写的书比较早,使用的开发环境比较旧,也许是我的学习还不到家,今天在看建议17的时候,发现了一些小问题,不是很大,是小问题,记录下来,当别人看到的时候可以起到修正的作用。

     可能很多人和我一样,没有太在乎for和foreach的区别,也没有深究其底层发生的一些东西,看了文章才发现里面的东西还真是不少。

    好了,我们从头聊一下,我对foreach的认识。

     Gof的23种设计模式,我相信大家都看过,我现在也正在写有关《设计模式》的一些文章。在这些设计模式种,有一个设计模式叫“迭代器”,这种设计模式就是针对集合对象的迭代而产生的。具体的模式我就不介绍了,FCL框架中也有针对的此模式的具体实现,具体的接口分别是IEnumerable和IEnumerator接口。迭代器模式屏蔽了各种集合的内部实现,为客户对集合的迭代生成了一个统一接口。foreach的使用语法就是针对FCL框架中实现的迭代器的统一操作实现,简化了语法,方便了客户的实现。

    通过该文章和对IL代码的分析,foreach除了可以提供简化语法外,还有另外两个有事:

      1、自动将代码置入try-finally块。

      2、若类型实现了IDisposable接口,他会在循环结束后自动调用Dispose方法。

   这是书里的原话,但是这两大优点是有出入的,并不是所有的集合类型都会将代码自动放入try-finally块中。

    我们先来了解一下集合概况吧,集合类型主要分为两种,一种是线性集合,另一种是非线性集合,如图:

     

  1、我们先针对线性集合测试,看看结果怎么样:

      1)、线性集合里面的直接存取类型的代表:数组

     

int[] arra = new int[] { 1, 2, 3, 4, 5, 6, 7 }; foreach (int item in arra) {     Console.WriteLine(item); }

  代码运行结果是:

  

我们再来看看他们所生成的IL代码:

.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // 代码大小       56 (0x38)  .maxstack  3  .locals init ([0] int32[] arra,           [1] int32[] V_1,           [2] int32 V_2,           [3] int32 item)  IL_0000:  nop  IL_0001:  ldc.i4.7  IL_0002:  newarr     [mscorlib]System.Int32  IL_0007:  dup  IL_0008:  ldtoken    field valuetype '
'/'__StaticArrayInitTypeSize=28' '
'::'447E020739FB3351B9350DB35F297A3AD27669A4' IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0012: stloc.0 IL_0013: nop IL_0014: ldloc.0 IL_0015: stloc.1 IL_0016: ldc.i4.0 IL_0017: stloc.2 IL_0018: br.s IL_002b IL_001a: ldloc.1 IL_001b: ldloc.2 IL_001c: ldelem.i4 IL_001d: stloc.3 IL_001e: nop IL_001f: ldloc.3 IL_0020: call void [mscorlib]System.Console::WriteLine(int32) IL_0025: nop IL_0026: nop IL_0027: ldloc.2 IL_0028: ldc.i4.1 IL_0029: add IL_002a: stloc.2 IL_002b: ldloc.2 IL_002c: ldloc.1 IL_002d: ldlen IL_002e: conv.i4 IL_002f: blt.s IL_001a IL_0031: call int32 [mscorlib]System.Console::Read() IL_0036: pop IL_0037: ret} // end of method Program::Main

   我们针对数组执行foreach迭代,但是在所生成的IL代码中并没有看到try-finally中,最起码连try和finally这个两个字样都没看到,书中说foreach遍历集合会自动生成try-finally块,这是不准确的,最起码数组没有生成try-finally代码块。

    2)、然后我们再测试一下string类型,看看他的运行结果怎么样!

       代码如下:

string dd = "我是中国人";foreach (var item in dd){    Console.WriteLine(item); }

 执行效果如图:

 

让我们再来看看它所生成的IL代码吧,不要惊讶:

.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // 代码大小       51 (0x33)  .maxstack  2  .locals init ([0] string dd,           [1] string V_1,           [2] int32 V_2,           [3] char item)  IL_0000:  nop  IL_0001:  ldstr      bytearray (11 62 2F 66 2D 4E FD 56 BA 4E )                   // .b/f-N.V.N  IL_0006:  stloc.0  IL_0007:  nop  IL_0008:  ldloc.0  IL_0009:  stloc.1  IL_000a:  ldc.i4.0  IL_000b:  stloc.2  IL_000c:  br.s       IL_0023  IL_000e:  ldloc.1  IL_000f:  ldloc.2  IL_0010:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)  IL_0015:  stloc.3  IL_0016:  nop  IL_0017:  ldloc.3  IL_0018:  call       void [mscorlib]System.Console::WriteLine(char)  IL_001d:  nop  IL_001e:  nop  IL_001f:  ldloc.2  IL_0020:  ldc.i4.1  IL_0021:  add  IL_0022:  stloc.2  IL_0023:  ldloc.2  IL_0024:  ldloc.1  IL_0025:  callvirt   instance int32 [mscorlib]System.String::get_Length()  IL_002a:  blt.s      IL_000e  IL_002c:  call       int32 [mscorlib]System.Console::Read()  IL_0031:  pop  IL_0032:  ret} // end of method Program::Main

  这里面也没有生成try-finally代码块,说明并不是所有的集合都会自动生成try-finally代码块的。

3)、我们继续测试一下Dictionary<TKey,TValue>,List<T>,Queue<T>,Stack<T>,ArrayList类型:

     (1)、Dictionary<TKey,TValue>测试代码:

Dictionary
dictionary = new Dictionary
(); dictionary.Add("1", "11"); dictionary.Add("2", "22"); dictionary.Add("3", "33"); dictionary.Add("4", "44"); dictionary.Add("5", "55"); dictionary.Add("6", "66"); dictionary.Add("7", "77"); foreach (var item in dictionary) { Console.WriteLine(item.Key + "----" + item.Value); }

      字典类型所生成IL代码如下:

.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // 代码大小       209 (0xd1)  .maxstack  3  .locals init ([0] class [mscorlib]System.Collections.Generic.Dictionary`2
dictionary, [1] valuetype [mscorlib]System.Collections.Generic.Dictionary`2/Enumerator
V_1, [2] valuetype [mscorlib]System.Collections.Generic.KeyValuePair`2
item) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldstr "1" IL_000d: ldstr "11" IL_0012: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_0017: nop IL_0018: ldloc.0 IL_0019: ldstr "2" IL_001e: ldstr "22" IL_0023: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_0028: nop IL_0029: ldloc.0 IL_002a: ldstr "3" IL_002f: ldstr "33" IL_0034: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_0039: nop IL_003a: ldloc.0 IL_003b: ldstr "4" IL_0040: ldstr "44" IL_0045: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_004a: nop IL_004b: ldloc.0 IL_004c: ldstr "5" IL_0051: ldstr "55" IL_0056: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_005b: nop IL_005c: ldloc.0 IL_005d: ldstr "6" IL_0062: ldstr "66" IL_0067: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_006c: nop IL_006d: ldloc.0 IL_006e: ldstr "7" IL_0073: ldstr "77" IL_0078: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2
::Add(!0, !1) IL_007d: nop IL_007e: nop IL_007f: ldloc.0 IL_0080: callvirt instance valuetype [mscorlib]System.Collections.Generic.Dictionary`2/Enumerator
class [mscorlib]System.Collections.Generic.Dictionary`2
::GetEnumerator() IL_0085: stloc.1 .try { IL_0086: br.s IL_00b0 IL_0088: ldloca.s V_1 IL_008a: call instance valuetype [mscorlib]System.Collections.Generic.KeyValuePair`2
valuetype [mscorlib]System.Collections.Generic.Dictionary`2/Enumerator
::get_Current() IL_008f: stloc.2 IL_0090: nop IL_0091: ldloca.s item IL_0093: call instance !0 valuetype [mscorlib]System.Collections.Generic.KeyValuePair`2
::get_Key() IL_0098: ldstr "----" IL_009d: ldloca.s item IL_009f: call instance !1 valuetype [mscorlib]System.Collections.Generic.KeyValuePair`2
::get_Value() IL_00a4: call string [mscorlib]System.String::Concat(string, string, string) IL_00a9: call void [mscorlib]System.Console::WriteLine(string) IL_00ae: nop IL_00af: nop IL_00b0: ldloca.s V_1 IL_00b2: call instance bool valuetype [mscorlib]System.Collections.Generic.Dictionary`2/Enumerator
::MoveNext() IL_00b7: brtrue.s IL_0088 IL_00b9: leave.s IL_00ca } // end .try finally { IL_00bb: ldloca.s V_1 IL_00bd: constrained. valuetype [mscorlib]System.Collections.Generic.Dictionary`2/Enumerator
IL_00c3: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_00c8: nop IL_00c9: endfinally } // end handler IL_00ca: call int32 [mscorlib]System.Console::Read() IL_00cf: pop IL_00d0: ret} // end of method Program::Main

  哈哈哈,终于出现了,自动生成try-finally代码块,并且调用了Dispose方法。

(2)List<T>,Queue<T>,Stack<T>,ArrayList测试代码如下:

    

List
list = new List
() { 1, 2 };foreach (var item in list) { Console.WriteLine(item);}
.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // 代码大小       83 (0x53)  .maxstack  3  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1
list, [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
V_1, [2] int32 item) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1
::.ctor() IL_0006: dup IL_0007: ldc.i4.1 IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
::Add(!0) IL_000d: nop IL_000e: dup IL_000f: ldc.i4.2 IL_0010: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
::Add(!0) IL_0015: nop IL_0016: stloc.0 IL_0017: nop IL_0018: ldloc.0 IL_0019: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
class [mscorlib]System.Collections.Generic.List`1
::GetEnumerator() IL_001e: stloc.1 .try { IL_001f: br.s IL_0032 IL_0021: ldloca.s V_1 IL_0023: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
::get_Current() IL_0028: stloc.2 IL_0029: nop IL_002a: ldloc.2 IL_002b: call void [mscorlib]System.Console::WriteLine(int32) IL_0030: nop IL_0031: nop IL_0032: ldloca.s V_1 IL_0034: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
::MoveNext() IL_0039: brtrue.s IL_0021 IL_003b: leave.s IL_004c } // end .try finally { IL_003d: ldloca.s V_1 IL_003f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
IL_0045: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_004a: nop IL_004b: endfinally } // end handler IL_004c: call int32 [mscorlib]System.Console::Read() IL_0051: pop IL_0052: ret} // end of method Program::Main

其他的代码我就不贴了,都生成了try-finally代码块,如果类型实现了IDisposable接口,也会调用Dispose方法。

2、我们再测试一下非线性集合是否会自动产生相关代码

    我们测试一下HashSet<T>代码,看看效果怎么样。

   

HashSet
hash = new HashSet
(); hash.Add("1"); hash.Add("2"); hash.Add("2"); hash.Add("3"); hash.Add("4"); hash.Add("5"); foreach (var item in hash) { Console.WriteLine(item); }

   运行效果图:

  

 最后我们看看IL代码:

  

.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // 代码大小       139 (0x8b)  .maxstack  2  .locals init ([0] class [System.Core]System.Collections.Generic.HashSet`1
hash, [1] valuetype [System.Core]System.Collections.Generic.HashSet`1/Enumerator
V_1, [2] string item) IL_0000: nop IL_0001: newobj instance void class [System.Core]System.Collections.Generic.HashSet`1
::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldstr "1" IL_000d: callvirt instance bool class [System.Core]System.Collections.Generic.HashSet`1
::Add(!0) IL_0012: pop IL_0013: ldloc.0 IL_0014: ldstr "2" IL_0019: callvirt instance bool class [System.Core]System.Collections.Generic.HashSet`1
::Add(!0) IL_001e: pop IL_001f: ldloc.0 IL_0020: ldstr "2" IL_0025: callvirt instance bool class [System.Core]System.Collections.Generic.HashSet`1
::Add(!0) IL_002a: pop IL_002b: ldloc.0 IL_002c: ldstr "3" IL_0031: callvirt instance bool class [System.Core]System.Collections.Generic.HashSet`1
::Add(!0) IL_0036: pop IL_0037: ldloc.0 IL_0038: ldstr "4" IL_003d: callvirt instance bool class [System.Core]System.Collections.Generic.HashSet`1
::Add(!0) IL_0042: pop IL_0043: ldloc.0 IL_0044: ldstr "5" IL_0049: callvirt instance bool class [System.Core]System.Collections.Generic.HashSet`1
::Add(!0) IL_004e: pop IL_004f: nop IL_0050: ldloc.0 IL_0051: callvirt instance valuetype [System.Core]System.Collections.Generic.HashSet`1/Enumerator
class [System.Core]System.Collections.Generic.HashSet`1
::GetEnumerator() IL_0056: stloc.1 .try { IL_0057: br.s IL_006a IL_0059: ldloca.s V_1 IL_005b: call instance !0 valuetype [System.Core]System.Collections.Generic.HashSet`1/Enumerator
::get_Current() IL_0060: stloc.2 IL_0061: nop IL_0062: ldloc.2 IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: nop IL_0069: nop IL_006a: ldloca.s V_1 IL_006c: call instance bool valuetype [System.Core]System.Collections.Generic.HashSet`1/Enumerator
::MoveNext() IL_0071: brtrue.s IL_0059 IL_0073: leave.s IL_0084 } // end .try finally { IL_0075: ldloca.s V_1 IL_0077: constrained. valuetype [System.Core]System.Collections.Generic.HashSet`1/Enumerator
IL_007d: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0082: nop IL_0083: endfinally } // end handler IL_0084: call int32 [mscorlib]System.Console::Read() IL_0089: pop IL_008a: ret} // end of method Program::Main

 

好了,总结一下吧,如果不涉及到修改集合元素,一般情况下使用Foreach比较好,在我测试的代码中,数组和String数据类型不会生成try-finally代码块,里面有一些出入而已,但是这并不影响Foreach的使用。所以我们在具体的编码过程中,尽量多的使用Foreach吧,没关系。

 

转载地址:http://opgwa.baihongyu.com/

你可能感兴趣的文章
python代码规范
查看>>
自动化运维工具Ansible实战(一)安装部署
查看>>
父元素与子元素的width关系
查看>>
史上最详细的vsftpd配置文件讲解
查看>>
Win8 Metro(C#)数字图像处理--2.70修正后的阿尔法滤波器
查看>>
用netstat -ano查看本机端口详解
查看>>
FineReport中如何自定义登录界面
查看>>
hadoop集群环境搭建
查看>>
严格就是大爱
查看>>
suse下xen记录
查看>>
FW Logging
查看>>
相对和绝对路径、cd命令、创建和删除目录、rm命令
查看>>
Python---函数---关键字参数
查看>>
spring context-config 与context-component-scan 的区别
查看>>
Linux添加永久静态路由
查看>>
PHP调试
查看>>
Routing TCP/IP
查看>>
xampp 访问出现New XAMPP security concept
查看>>
销售排名
查看>>
OpenWRT刷固件
查看>>