专业汉语词典知识平台,分享汉字词语知识、历史文学知识解答!

励北网
励北网

params参数是什么,params注解

来源:小易整编  作者:小易  发布时间:2023-03-02 04:43
摘要:params参数是什么,params注解一:params应用层玩法首先上一段测试代码。classProgram staticvoidMain(string[]args) Test(100,200,300)...

params参数是什么,params注解

params参数是什么,params注解

一:params 应用层玩法

首先上一段 测试代码。

class Program    {        static void Main(string[] args)        {            Test(100, 200, 300);            Test();        }        static void Test(params int[] list)        {            Console.WriteLine($"list.length={list.Length}");        }    }

输出结果如下:

可以看出如果给 方法形参 加上 params 前缀,在传递 方法实参 上就特别灵活,点赞。

接下来我们来看看,这么灵活的 实参传递 底层到底是怎么玩的?我们先从 IL 层面探究下。

二:IL 层面解读

用 ILSpy 打开我们的 exe ,看看 IL 代码

.method private hidebysig static void Main (  string[] args ) cil managed { // Method begins at RVA 0x2050 // Code size 37 (0x25) .maxstack 8 .entrypoint IL_0000: nop IL_0001: ldc.i4.3 IL_0002: newarr [mscorlib]System.Int32 IL_0007: dup IL_0008: ldtoken field valuetype '<PrivateImplementationDetails>'/'__StaticArrayInitTypeSize=12' '<PrivateImplementationDetails>'::'9AC9CF706FBD14D039E0436219C5D852927E5F69295F2EF423AE897345197B2A' IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0012: call void ConsoleApp2.Program::Test(int32[]) IL_0017: nop IL_0018: ldc.i4.0 IL_0019: newarr [mscorlib]System.Int32 IL_001e: call void ConsoleApp2.Program::Test(int32[]) IL_0023: nop IL_0024: ret } // end of method Program::Main .method private hidebysig static void Test (  int32[] list ) cil managed { .param [1]  .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = (   01 00 00 00  ) // Method begins at RVA 0x2076 // Code size 26 (0x1a) .maxstack 8 IL_0000: nop IL_0001: ldstr "list.length={0}" IL_0006: ldarg.0 IL_0007: ldlen IL_0008: conv.i4 IL_0009: box [mscorlib]System.Int32 IL_000e: call string [mscorlib]System.String::Format(string, object) IL_0013: call void [mscorlib]System.Console::WriteLine(string) IL_0018: nop IL_0019: ret } // end of method Program::Test

上面是 Main 和 Test 方法的IL代码,我们逐一看一下。

1. Test 方法

从 int32[] list 参数看并没有所谓的 params,这也就说明它是 C#编译器 玩的一个手段而已,在方法调用前就已经构建好了。

2. Main方法

可以看到 IL 层面的 Test(100, 200, 300) 已经变成了下面五行代码。

IL_0002: newarr [mscorlib]System.Int32 IL_0007: dup IL_0008: ldtoken field valuetype '<PrivateImplementationDetails>'/'__StaticArrayInitTypeSize=12' '<PrivateImplementationDetails>'::'9AC9CF706FBD14D039E0436219C5D852927E5F69295F2EF423AE897345197B2A' IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0012: call void ConsoleApp2.Program::Test(int32[])

逻辑大概就是:

  • 用 newarr 构建初始化 int[] 数组。
  • 用 ldtoken 从程序元数据中提取 1,2,3。
  • 用 InitializeArray 来将 1,2,3 构建到数组中。

有了这些指令,我相信 JIT 就知道怎么做了。

再看 Test() 的 IL 指令只有一行 newarr [mscorlib]System.Int32 初始化。

所以本质上来说,就是提前构建好 array,然后进行参数传递,仅此而已。。。

三:汇编层面解读

有了 newarr + ldtoken + call 三条指令,接下来我们读一下汇编层做了什么,使用 windbg 打开 exe,简化后的汇编代码如下:

0:000> !U /d 009b0848 Normal JIT generated code ConsoleApp2.Program.Main(System.String[]) Begin 009b0848, size 77 D:\net5\ConsoleApp4\ConsoleApp2\Program.cs @ 14: >>> 009b0848 55              push    ebp 009b0849 8bec            mov     ebp,esp 009b084b 83ec10          sub     esp,10h 009b084e 33c0            xor     eax,eax 009b0850 8945f4          mov     dword ptr [ebp-0Ch],eax 009b0853 8945f8          mov     dword ptr [ebp-8],eax 009b0856 8945f0          mov     dword ptr [ebp-10h],eax 009b0859 894dfc          mov     dword ptr [ebp-4],ecx 009b085c 833df042710000  cmp     dword ptr ds:[7142F0h],0 009b0863 7405            je      009b086a 009b0865 e816f55264      call    clr!JIT_DbgIsJustMyCode (64edfd80) 009b086a 90              nop D:\net5\ConsoleApp4\ConsoleApp2\Program.cs @ 15: 009b086b b95e186763      mov     ecx,offset mscorlib_ni!System.Text.Encoding.GetEncodingCodePage(Int32)$##6006719 <PERF> (mscorlib_ni+0x185e) (6367185e) 009b0870 ba03000000      mov     edx,3 009b0875 e8b229d5ff      call    0070322c (JitHelp: CORINFO_HELP_NEWARR_1_VC) 009b087a 8945f4          mov     dword ptr [ebp-0Ch],eax 009b087d b9e04d7100      mov     ecx,714DE0h 009b0882 e819c91f64      call    clr!JIT_GetRuntimeFieldStub (64bad1a0) 009b0887 8945f8          mov     dword ptr [ebp-8],eax 009b088a 8d45f8          lea     eax,[ebp-8] 009b088d ff30            push    dword ptr [eax] 009b088f 8b4df4          mov     ecx,dword ptr [ebp-0Ch] 009b0892 e8f9c61f64      call    clr!ArrayNative::InitializeArray (64bacf90) 009b0897 8b4df4          mov     ecx,dword ptr [ebp-0Ch] 009b089a ff15904d7100    call    dword ptr ds:[714D90h] (ConsoleApp2.Program.Test(Int32[]), mdToken: 06000002) 009b08a0 90              nop D:\net5\ConsoleApp4\ConsoleApp2\Program.cs @ 16: 009b08a1 b95e186763      mov     ecx,offset mscorlib_ni!System.Text.Encoding.GetEncodingCodePage(Int32)$##6006719 <PERF> (mscorlib_ni+0x185e) (6367185e) 009b08a6 33d2            xor     edx,edx 009b08a8 e87f29d5ff      call    0070322c (JitHelp: CORINFO_HELP_NEWARR_1_VC) 009b08ad 8945f0          mov     dword ptr [ebp-10h],eax 009b08b0 8b4df0          mov     ecx,dword ptr [ebp-10h] 009b08b3 ff15904d7100    call    dword ptr ds:[714D90h] (ConsoleApp2.Program.Test(Int32[]), mdToken: 06000002) 009b08b9 90              nop D:\net5\ConsoleApp4\ConsoleApp2\Program.cs @ 17: 009b08ba 90              nop 009b08bb 8be5            mov     esp,ebp 009b08bd 5d              pop     ebp 009b08be c3              ret

1. newarr

可以很清楚的看到,newarr 调用了 CLR 中的jithelper函数 CORINFO_HELP_NEWARR_1_VC 下的  JIT_NewArr1 方法,大家有兴趣可以看下 jitheapler.cpp,调用完之后,初始化数组就出来了。

从dp看,数组只申明了 length=3,还并没有数组元素,也就说所谓的初始化。

2. ldtoken & InitializeArray

刚才也说到了, ldtoken 是在运行时提取元数据,那就必须要解析 PE 头,在 clr 层面有一个 PEDecoder::GetRvaData 方法就是用来解析运行时数据,它是发生在 ArrayNative::InitializeArray 方法中,所以我们下两个 bu 命令拦截。

0:000> bu clr!ArrayNative::InitializeArray 0:000> bu clr!PEDecoder::GetRvaData 0:000> g Breakpoint 3 hit eax=0019f500 ebx=0019f5ac ecx=024c2338 edx=006fb930 esi=00000000 edi=0019f520 eip=64bacf90 esp=0019f4f0 ebp=0019f508 iopl=0         nv up ei pl zr na pe nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246 clr!ArrayNative::InitializeArray: 64bacf90 6a78            push    78h 0:000> g Breakpoint 0 hit eax=00713448 ebx=00624044 ecx=0071344c edx=0071dc28 esi=00624044 edi=00624de0 eip=64befcfc esp=0019f400 ebp=0019f410 iopl=0         nv up ei pl zr na pe nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246 clr!PEDecoder::GetRvaData: 64befcfc 55              push    ebp 0:000> k # ChildEBP RetAddr       00 0019f410 64b63be5     clr!PEDecoder::GetRvaData 01 0019f410 64b63bb3     clr!Module::GetRvaField+0x40 02 0019f44c 64bad0a7     clr!FieldDesc::GetStaticAddressHandle+0xdd 03 0019f4ec 00680897     clr!ArrayNative::InitializeArray+0x11a 0:000> bp 64b63be5 0:000> g eax=00402928 ebx=00624044 ecx=0071344c edx=0071dc28 esi=00624044 edi=00624de0 eip=64b63be5 esp=0019f40c ebp=0019f410 iopl=0         nv up ei pl nz na pe nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206 clr!Module::GetRvaField+0x40: 64b63be5 5e              pop     esi

从输出看,上面的 GetRvaField 方法的返回值会送到 eax 上,接下来我们验证下 eax 上的值是不是参数 100,200,300 。

0:000> dp eax L3 00402928  00000064 000000c8 0000012c

上面三个就是 16进制的表示,接下来我们再验证下这三个值是怎么赋到初始化数组中的,可以用 ba 命令对 内存地址 进行拦截。

0:000> ba r4 023e2338 + 0x8 0:000> ba r4 023e2338 + 0x8 + 0x4 0:000> ba r4 023e2338 + 0x8 + 0x4 + 0x4 0:000> g Breakpoint 3 hit eax=0000000c ebx=00000000 ecx=00000003 edx=00000064 esi=00402928 edi=023e2340 eip=6a91d68b esp=0019f440 ebp=0019f4ec iopl=0         nv up ei pl nz na pe nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206 VCRUNTIME140_CLR0400!memcpy+0x50b: 6a91d68b 83c704          add     edi,4 0:000> g Breakpoint 4 hit eax=0000000c ebx=00000000 ecx=00000002 edx=000000c8 esi=0040292c edi=023e2344 eip=6a91d68b esp=0019f440 ebp=0019f4ec iopl=0         nv up ei pl nz na po nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202 VCRUNTIME140_CLR0400!memcpy+0x50b: 6a91d68b 83c704          add     edi,4 0:000> g Breakpoint 5 hit eax=0000000c ebx=00000000 ecx=00000001 edx=0000012c esi=00402930 edi=023e2348 eip=6a91d68b esp=0019f440 ebp=0019f4ec iopl=0         nv up ei pl nz na po nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202 VCRUNTIME140_CLR0400!memcpy+0x50b: 6a91d68b 83c704          add     edi,4 0:000> dp 023e2338 L5 023e2338  6368426c 00000003 00000064 000000c8 023e2348  0000012c

接下来稍微解释下 ba r4 023e2338 + 0x8 命令。

  • 023e2338 是初始化数组的首地址。

  • 023e2338+0x8 初始化数组第一个元素的地址。

  • 023e2338 + 0x8 + 0x4  初始化数组第二个元素的地址。

  • r4  按4byte对地址块读写进行监控。

当三个断点命中后,可以看到初始化数组 023e2338 上的三个元素值都已经填上了,就说这么多吧,相信大家对 params 机制有一定的理解。


本文地址:百科问答频道 https://www.neebe.cn/wenda/907983.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!


百科问答
小编:小易整编
相关文章相关阅读
  • 小米平板3配置参数如何

    小米平板3配置参数如何

    小米平板3配置参数如何  自从小米平板2推出后,其2g内存直让人喊渣渣。这回小米平板3直接上台阶,采用了8GBLPDDR3内存和128GB/256GB机身存储。  而处理器搭载了英特尔第七代酷睿M处理器M3-7Y30,运行Win...

  • OPPO A1详细参数分析

    OPPO A1详细参数分析

    OPPOA1于2018年4月1日正式上市,这款搭载ColorOS系统,基于安卓7.1的千元机究竟表现如何,能否在千元机市场上站住脚,能否满足千元机用户的需求,让我们一起来看看吧。这款手机的参数表现如何。这款手机有两种配置3+32GB的配置...

  • 内存条看什么参数

    内存条看什么参数

    选购内存条先明确是否为笔记本内存条,并且测试电脑主板所支持的内存条类型是DDR3或DDR4,如果是升级内存条需要查看原有内存条的频率和参数,尽可能选购一样的批量和品牌。最后根据自己需求和运算,选择主板所支持范围内尽可能选跟大的品牌内存。内存...

  • 如何配置OpenWrt的网络参数

    如何配置OpenWrt的网络参数

    本文详细介绍了如何配置OpenWrt的网络参数,包括LAN和WAN设置、静态IP地址配置、DHCP服务器设置以及防火墙配置。通过正确配置这些参数,用户可以搭建和管理自己的网络环境,并实现各种网络需求。OpenWrt是一种基于Linux...

  • 逆水寒手游捏脸数据介绍 捏脸参数有哪些

    逆水寒手游捏脸数据介绍 捏脸参数有哪些

    逆水寒手游可以说是古装武侠风的角色扮演向的移动端游戏,玩家可以在游戏中自由地创建自己的武侠形象,体验江湖的恩怨情仇。小编为大家介绍的是逆水寒手游捏脸数据介绍,捏脸系统是游戏中最吸引人的功能之一,它可以让玩家根据自己的喜好,调整角色的五官、发...

  • 索爱k858参数详情分析

    索爱k858参数详情分析

    说起索爱k858可能很多人都一头雾水吧,其实这款手机的全称是索尼爱立信k858c哦,这下子大家应该有一个了解了吧。这款手机是2007年出产的,虽然现在我们的社会都是以用智能手机为主,但是社会中还是有一部分人用不习惯智能机,喜欢用以前的...

  • 华硕x550c配置怎么样 华硕笔记本参数配置介绍

    华硕x550c配置怎么样 华硕笔记本参数配置介绍

    导语:很多人对于华硕有很大的好感,比如小编也是,因为华硕的笔记本性价比比较高,而且在各方面也做得很好,深受学生的喜爱,而今天我们要说的是华硕x550c配置,那么现在跟着小编一起来看看吧。华硕x550c系列价格Intel酷睿i34...

  • 主机箱多少钱?产品参数及功能特点介绍

    主机箱多少钱?产品参数及功能特点介绍

    导语:配置一款先进的电脑,首先要选购合格的外部设备,诸如显示屏、鼠标、键盘、主机箱等等。有追求个性的朋友会选择比较时尚和美观的主机箱,包括其他的外部设备。今天小编为大家介绍一下目前市场上出现的最新款式、风格的主机箱,其主要价格是多少,希望...

  • 周排行
  • 月排行
  • 年排行

精彩推荐