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类型,但静态调度的调用使用一个用于值类型.这是丑陋的,不太可能,但可能.
>在vtable中,它存储一个“包装”方法,将其解包,然后调用实际方法.这听起来效率低下,因为所有方法的参数都必须通过两次调用来复制.
>有一些特殊的逻辑可以在callvirt中检查这一点.效率更低:所有的callvirts都会受到轻微的惩罚.

解决方法

简短的回答是,在方法本身中,结构的值总是通过指针访问.这意味着该方法不像结构作为普通参数传递一样运行,它更像是一个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).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1:最直白的循环遍历方法,可以分为遍历key--value键值对以及所有的key两种表现形式2:用Linq的方式去查询(当然了这里要添加对应的命名空间 using System.Linq)如下为一个十分简单的代码示例:private void GetDicKeyByValue(){ Dicti...
private void ClearTextBox(){ foreach (var control in pnlDetail.Controls) { if (!(control is TextBox)) continue; var txtBox = (TextBox)control; txtBox.
原文叫看《墨攻》理解IOC概念 2006年多部贺岁大片以让人应接不暇的频率纷至沓来,其中张之亮的《墨攻》算是比较出彩的一部,讲述了战国时期墨家人革离帮助梁 国反抗赵国侵略的个人英雄主义故事,恢宏壮阔,浑雄凝重的历史场面相当震撼。其中有一个场景:当刘德华所饰的墨者革离到达梁国都城 下,城上梁国守军问:
System.Data.ConstraintException: Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.
Request.Form value was detected from the client在使用ASP.NET MVC3 开发系统的用了百度的UEditor编辑器提交表单时遇到检测到有潜在危险的 Request.Form,我百度一下,试了网上的方法,都没用。要在Web.config增加<h
右击文件夹->安全选项卡->添加->高级->立即查找Windows Server 2003:请您在目录添加IIS来宾帐号(IUSR_Hostname)的只读权限,以及Network Service组的读写修改权限。Windows Server 2000:请您在目录添加IIS来
<compilationdebug="true"><buildProviders><addextension=".html"type="System.Web.Compilation.PageBuildProvider&q
在ASP.NET MVC 中 Spring.NET 配置注入的时候,下面这方式是可行的。<spring> <context> <resource uri="config://spring/objects" /> </context>
Stopwatch stopwatch = new Stopwatch();stopwatch.Start();。。。。。中间代码。。。	stopwatch.Stop();	long result = stopwatch.ElapsedMilliseconds;sqlBulkCopy.Close()
问题描述 在asp.net mvc 下配置ueditor图片上传时总是提示:缺少十六进制字符错误(IE下提示),起初还以为是我上传的图片名称中有中文字符所致,后来我又上传了英文字符名字的图片发现还是一样的错误提示:◆无赖之下只好到火狐下看错误的详情:◆第二个错误我无法解决,先看第一个,输入地址显示错
已经安装net2.0 和3.5 ,但IIS里面却只有1.1开始→运行→CMDC:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe -i按回车键后便会开始自动安装,安装完重启一下IIS在IIS中ASP.NET选项卡便可以看到。注
根据传进来不同的值,调用不同的方法View Code protected void btn_SwitchClick(object sender, EventArgs e){ string result = ""; switch (ddlMethod.SelectedValue)
整理两个 在C#中,用正则表达式 获取网页源代码标签的属性或值的方法 :1、获取标签中的值: CSDN 结果:CSDN/// /// 获取字符中指定标签的值 /// /// 字符串 /// 标签 /// 值 public static string GetTitleContent(string st
/// <summary>/// 集合装换DataTable/// </summary>/// <param name="list">集合</param>/// <returns></returns>publ
将目录下面所有的 .cs 文件合并到一个 code.cs 文件中,写著作权复制代码时的必备良药 @echo off echo 将该目录下所有.cs文件的内容合并到一个 code.cs 文件中! pause dir /ad/s/b > folderPath.txt md codeTemp for
做接口开发的时候,往往接受参数或返回值是一个XML的字符串。如下图,不方便辨识 两种方法, 1.将它保存为xxx.xml,然后用浏览器打开。这种方法稍微有些麻烦。 2.使用 UltraEdit 工具
一个字段控制多个状态选项private void GenerateAdvice_OnClick(object sender, RoutedEventArgs e){ TestStatus c1 = TestStatus.A | TestStatus.C | TestStatus.E; v...
效果如下: 代码如下:
StartUp.cs public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseEndpoints(endpoints => { endpoints.MapControllerRoute(