C#如何处理在结构上调用接口方法?
考虑:
interface I { void M(); } struct S: I { public void M() {} } // in Main: S s; I i = s; s.M(); i.M(); 以及IL for Main: .maxstack 1 .entrypoint .locals init ( [0] valuetype S s,[1] class I i ) IL_0000: nop IL_0001: ldloc.0 IL_0002: box S IL_0007: stloc.1 IL_0008: ldloca.s s IL_000a: call instance void S::M() IL_000f: nop IL_0010: ldloc.1 IL_0011: callvirt instance void I::M() IL_0016: nop IL_0017: ret 首先(IL_000a),S :: M()用它的值类型调用.接下来(IL_0011),使用引用(盒装)类型调用它. 这是如何运作的? 我可以想到三种方式: >编译两个版本的I :: M,用于value / ref类型.在vtable中,它存储一个用于ref类型,但静态调度的调用使用一个用于值类型.这是丑陋的,不太可能,但可能. 解决方法
简短的回答是,在方法本身中,结构的值总是通过指针访问.这意味着该方法不像结构作为普通参数传递一样运行,它更像是一个ref参数.这也意味着该方法不知道它是否在盒装值上运行.
答案很长: 首先,如果我编译你的代码,那么s.M();不生成任何代码. JIT编译器非常智能,可以内联方法,并且内联空方法不会导致代码.所以,我所做的是在S.M上应用[MethodImpl(MethodImplOptions.NoInlining)]来避免这种情况. 现在,这是您的方法生成的本机代码(省略函数prolog和epilog): // initialize s in register AX xor eax,eax // move s from register AX to stack (SP+28h) mov qword ptr [rsp+28h],rax // load pointer to MethodTable for S to register CX mov rcx,7FFDB00C5B08h // allocate memory for i on heap call JIT_TrialAllocSFastMP_InlineGetThread (07FFE0F824C10h) // copy contents of s from stack to register C movsx rcx,byte ptr [rsp+28h] // copy from register CX to heap mov byte ptr [rax+8],cl // copy pointer to i from register AX to register SI mov rsi,rax // load address to c on stack to register CX lea rcx,[rsp+28h] // call S::M call 00007FFDB01D00C8 // copy pointer to i from register SI to register CX mov rcx,rsi // move address of stub for I::M to register 11 mov r11,7FFDB00D0020h // ??? cmp dword ptr [rcx],ecx // call stub for I::M call qword ptr [r11] 在这两种情况下,调用最终都会调用相同的代码(这只是一个ret指令).第一次,CX寄存器指向堆栈分配的s(上面代码中的SP 28h),第二次指向堆分配的i(刚刚调用堆分配函数后的AX 8). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |