初读 c# IL中间语言

对一段c#编写的代码,有一些疑问,想通过IL中间语言看看,编译后是怎么处理的。代码如下:

 1         static StringBuilder sb = new StringBuilder();
 2 
 3         static int filecount = 0;
 4         static int dirCount = 0;
 5 
 6         /// <summary>  
 7         /// 获取目录path下所有子文件名  
 8         /// </summary>  
 9         public static List<string> getAllFiles(String path, int depth = 0)
10         {
11             List<string> strs = new List<string>();
12             if (System.IO.Directory.Exists(path))
13             {
14                 //所有子文件名  
15                 string[] files = System.IO.Directory.GetFiles(path);
16                 foreach (string file in files)
17                 {
18                     sb.Append(new string('*', depth * 3) + file + "\r\n");
19                     strs.Add(file);
20                     filecount++;
21                 }
22                 //所有子目录名  
23                 string[] Dirs = System.IO.Directory.GetDirectories(path);
24                 foreach (string dir in Dirs)
25                 {
26                     dirCount++;
27                     sb.Append(new string('*', depth * 3) + dir + "\r\n");
28                     var tmp = getAllFiles(dir, depth + 1);  //子目录下所有子文件名  
29                     if (!tmp.Equals(""))
30                     {
31                         strs.AddRange(tmp);
32                     }
33                 }
34             }
35             return strs;
36         }

       这段代码的功能是很简单的:给定一个文件夹,返回下面的所有文件(递归遍历)。我的疑问:在第11行,递归调用的时候,strs变量(用来存放所有文件的名称列表)能够保存到所有文件名吗?程序运行的结果告诉我,代码没有任何问题。代码是别人写的,如果是我写的话,可能会在方法之外定义一个变量,在递归的时候,遇到文件的时候,把名字里存进去,而递归方法就不用返回值。当然按照依赖关系来看的话,方法内部用的变量,要么是内部自定义的,要么是参数传进来的,这样对外没有依赖,方法的复用性高。毋庸置疑,代码没啥问题。第一行的sb是我加进去的。通过查看IL代码,明白了一些道理,看到了编译器为我们程序处理的一些东西,比较感兴趣,就把这段IL摘下来:

.method public hidebysig static class [mscorlib]System.Collections.Generic.List`1<string> 
        getAllFiles(string path,
                    [opt] int32 depth) cil managed
{
  .param [2] = int32(0x00000000)
  // 代码大小       262 (0x106)
  .maxstack  4
  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<string> strs,
           [1] string[] files,
           [2] string file,
           [3] string[] Dirs,
           [4] string dir,
           [5] class [mscorlib]System.Collections.Generic.List`1<string> tmp,
           [6] class [mscorlib]System.Collections.Generic.List`1<string> CS$1$0000,
           [7] bool CS$4$0001,
           [8] string[] CS$6$0002,
           [9] int32 CS$7$0003)          // 带$符号的变量是编译器定义的临时变量
  IL_0000:  nop
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
  IL_0006:  stloc.0     //把上面实例化好的对象保存到第一个变量中,也就是strs
  IL_0007:  ldarg.0     //加载第一个参数,也就是path
  IL_0008:  call       bool [mscorlib]System.IO.Directory::Exists(string)
  IL_000d:  ldc.i4.0    //在堆栈中加载一个int类型,值为0的常量
  IL_000e:  ceq          
  IL_0010:  stloc.s    CS$4$0001   //把 call的结果和0作比较,把比较的结果存到这个变量中
  IL_0012:  ldloc.s    CS$4$0001   //加载这个变量到堆栈中
  IL_0014:  brtrue     IL_00fe     //如果call的结果和0相等就跳转了,也就是说001中存了true
  IL_0019:  nop
  IL_001a:  ldarg.0               
  IL_001b:  call       string[] [mscorlib]System.IO.Directory::GetFiles(string)
  IL_0020:  stloc.1         //存储到第一个变量:files
  IL_0021:  nop
  IL_0022:  ldloc.1         //加载到第一个变量到堆栈里
  IL_0023:  stloc.s    CS$6$0002    //把这个变量的值存到002变量中
  IL_0025:  ldc.i4.0                
  IL_0026:  stloc.s    CS$7$0003    //把0存储到003的变量中
  IL_0028:  br.s       IL_006c       //跳转到006c行
  IL_002a:  ldloc.s    CS$6$0002      //加载数组
  IL_002c:  ldloc.s    CS$7$0003      //加载003,此时值为0
  IL_002e:  ldelem.ref                //取数组中的一个元素
  IL_002f:  stloc.2                   //存到变量2 file中
  IL_0030:  nop
  IL_0031:  ldsfld     class [mscorlib]System.Text.StringBuilder Client.Program::sb    //加载sb字段
  IL_0036:  ldc.i4.s   42    //加载常量42
  IL_0038:  ldarg.1          //加载第二个参数depth
  IL_0039:  ldc.i4.3         //加载常量3
  IL_003a:  mul              //3*depth 乘法
  IL_003b:  newobj     instance void [mscorlib]System.String::.ctor(char,
                                                                    int32)
  IL_0040:  ldloc.2
  IL_0041:  ldstr      "\r\n"
  IL_0046:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)     //实例化了string的实例,并把前面3个字符串给拼接起来
  IL_004b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0050:  pop
  IL_0051:  ldloc.0      //加载第一个变量strs
  IL_0052:  ldloc.2      //加载第三个变量file
  IL_0053:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
  IL_0058:  nop
  IL_0059:  ldsfld     int32 Client.Program::filecount
  IL_005e:  ldc.i4.1
  IL_005f:  add           //filecount加1
  IL_0060:  stsfld     int32 Client.Program::filecount
  IL_0065:  nop
  IL_0066:  ldloc.s    CS$7$0003   加载了003,值为0
  IL_0068:  ldc.i4.1
  IL_0069:  add
  IL_006a:  stloc.s    CS$7$0003   给003加了1,此时003的值为1,这么看来003就是个计数器,用来控制循环
  IL_006c:  ldloc.s    CS$7$0003         //加载003,第一次,变量中为0
  IL_006e:  ldloc.s    CS$6$0002         //加载002,此时为files数组
  IL_0070:  ldlen                        //取数组长度,并转为int类型
  IL_0071:  conv.i4
  IL_0072:  clt
  IL_0074:  stloc.s    CS$4$0001          //看003是否比数组长度小,把比较结果存到001中
  IL_0076:  ldloc.s    CS$4$0001
  IL_0078:  brtrue.s   IL_002a            //如果为true,跳转到002a行
  IL_007a:  ldarg.0
  IL_007b:  call       string[] [mscorlib]System.IO.Directory::GetDirectories(string)
  IL_0080:  stloc.3
  IL_0081:  nop
  IL_0082:  ldloc.3
  IL_0083:  stloc.s    CS$6$0002
  IL_0085:  ldc.i4.0
  IL_0086:  stloc.s    CS$7$0003
  IL_0088:  br.s       IL_00ef
  IL_008a:  ldloc.s    CS$6$0002
  IL_008c:  ldloc.s    CS$7$0003
  IL_008e:  ldelem.ref
  IL_008f:  stloc.s    dir
  IL_0091:  nop
  IL_0092:  ldsfld     int32 Client.Program::dirCount
  IL_0097:  ldc.i4.1
  IL_0098:  add
  IL_0099:  stsfld     int32 Client.Program::dirCount
  IL_009e:  ldsfld     class [mscorlib]System.Text.StringBuilder Client.Program::sb
  IL_00a3:  ldc.i4.s   42
  IL_00a5:  ldarg.1
  IL_00a6:  ldc.i4.3
  IL_00a7:  mul
  IL_00a8:  newobj     instance void [mscorlib]System.String::.ctor(char,
                                                                    int32)
  IL_00ad:  ldloc.s    dir
  IL_00af:  ldstr      "\r\n"
  IL_00b4:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)
  IL_00b9:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_00be:  pop
  IL_00bf:  ldloc.s    dir
  IL_00c1:  ldarg.1
  IL_00c2:  ldc.i4.1
  IL_00c3:  add
  IL_00c4:  call       class [mscorlib]System.Collections.Generic.List`1<string> Client.Program::getAllFiles(string,
                                                                                                             int32)
  IL_00c9:  stloc.s    tmp
  IL_00cb:  ldloc.s    tmp
  IL_00cd:  ldstr      ""
  IL_00d2:  callvirt   instance bool [mscorlib]System.Object::Equals(object)
  IL_00d7:  stloc.s    CS$4$0001
  IL_00d9:  ldloc.s    CS$4$0001
  IL_00db:  brtrue.s   IL_00e8
  IL_00dd:  nop
  IL_00de:  ldloc.0
  IL_00df:  ldloc.s    tmp
  IL_00e1:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<string>::AddRange(class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>)
  IL_00e6:  nop
  IL_00e7:  nop
  IL_00e8:  nop
  IL_00e9:  ldloc.s    CS$7$0003
  IL_00eb:  ldc.i4.1
  IL_00ec:  add
  IL_00ed:  stloc.s    CS$7$0003
  IL_00ef:  ldloc.s    CS$7$0003
  IL_00f1:  ldloc.s    CS$6$0002
  IL_00f3:  ldlen
  IL_00f4:  conv.i4
  IL_00f5:  clt
  IL_00f7:  stloc.s    CS$4$0001
  IL_00f9:  ldloc.s    CS$4$0001
  IL_00fb:  brtrue.s   IL_008a
  IL_00fd:  nop
  IL_00fe:  ldloc.0
  IL_00ff:  stloc.s    CS$1$0000
  IL_0101:  br.s       IL_0103
  IL_0103:  ldloc.s    CS$1$0000
  IL_0105:  ret
} // end of method Program::getAllFiles

我加注释的地方,就是方法从头开始到第一个循环结束的地方,后面的循环和这个类似,就不解读了。

posted @ 2017-12-05 18:47  micDavid  阅读(395)  评论(0编辑  收藏  举报