可以使用C#泛型来取消虚拟函数调用吗?

如何解决可以使用C#泛型来取消虚拟函数调用吗?

| 我同时使用C ++和C#,并且一直想着是否可以在C#中使用泛型来消除接口上的虚函数调用。考虑以下:
int Foo1(IList<int> list)
{
    int sum = 0;
    for(int i = 0; i < list.Count; ++i)
        sum += list[i];
    return sum;
}

int Foo2<T>(T list) where T : IList<int>
{
    int sum = 0;
    for(int i = 0; i < list.Count; ++i)
        sum += list[i];
    return sum;
}

/*...*/
var l = new List<int>();
Foo1(l);
Foo2(l);
在Foo1内部,对list.Count和list [i]的每次访问都会导致虚拟函数调用。如果这是使用模板的C ++,则在对Foo2的调用中,编译器将能够看到可以省略和内联虚拟函数调用,因为具体类型在模板实例化时是已知的。 但是,这是否同样适用于C#和泛型?当您调用Foo2(l)时,在编译时就知道T是一个List,因此该list.Count和list [i]不需要涉及虚拟函数调用。首先,这将是不会可怕地破坏某些东西的有效优化吗?如果是这样,那么编译器/ JIT是否足够聪明以进行优化?     

解决方法

        这是一个有趣的问题,但是不幸的是,您“欺骗”系统的方法不会提高程序的效率。如果可以的话,编译器可以相对轻松地为我们做到! 您是正确的,当通过接口引用调用
IList<T>
时,方法是在运行时分派的,因此无法内联。因此,将通过接口调用对诸如“ 3”之类的“ 1”方法和索引器的调用。 另一方面,通过将其重写为通用方法,可以实现任何性能优势(至少不能通过当前的C#编译器和.NET4 CLR实现)是不正确的。 为什么不?首先介绍一些背景。 C#泛型的工作是编译器编译具有可替换参数的泛型方法,然后在运行时将其替换为实际参数。这个你已经知道了。 但是该方法的参数化版本比您和我在编译时更了解变量类型。在这种情况下,所有编译器都知道
Foo2
list
IList<int>
。通用the4中的信息与非通用ѭ8中的信息相同。 实际上,为了避免代码膨胀,JIT编译器仅对所有引用类型生成通用方法的单个实例。这是描述此替换和实例化的Microsoft文档:   如果客户端指定了引用类型,则JIT编译器将服务器IL中的通用参数替换为Object,然后将其编译为本机代码。该代码将在对引用类型的任何进一步请求中使用,而不是在泛型类型参数中使用。注意,通过这种方式,JIT编译器仅重用实际代码。实例仍根据其在托管堆上的大小分配,并且没有强制转换。 这意味着JIT编译器的方法版本(对于引用类型)不是类型安全的,但并不重要,因为编译器已在编译时确保了所有类型安全。但是,对于您的问题更重要的是,没有任何方法可以执行内联并获得性能提升。 编辑:最后,根据经验,我刚刚完成了
Foo1
和ѭ4done的基准测试,它们产生了相同的性能结果。换句话说,
Foo2
并不比
Foo1
快。 让我们添加一个“ inlinable”版本
Foo0
进行比较:
int Foo0(List<int> list)
{
    int sum = 0;
    for (int i = 0; i < list.Count; ++i)
        sum += list[i];
    return sum;
}
这是性能比较:
Foo0 = 1719
Foo1 = 7299
Foo2 = 7472
Foo0 = 1671
Foo1 = 7470
Foo2 = 7756
因此,您可以看到可以内联的
Foo0
比其他两个要快得多。您还可以看到
Foo2
稍慢一些,而不是
Foo0
一样快。     ,        这确实有效,并且(如果函数不是虚拟的)会导致非虚拟的调用。原因是与C ++不同,CLR泛型在JIT时为每个唯一的泛型参数集定义了一个具体的具体类(通过尾随1、2等的反射来表示)。如果该方法是虚拟的,则它将导致虚拟调用,就像任何具体的,非虚拟的,非通用的方法一样。 关于.net泛型要记住的是:
Foo<T>; 
然后
Foo<Int32>
在运行时是有效的Type,与
Foo<String>
,并且相应地处理了所有虚拟和非虚拟方法。这就是为什么您可以创建一个
List<Vehicle>
并向其添加Car,但是您无法创建类型为
List<Vehicle> 
并将其值设置为的实例
List<Car>
。它们的类型不同,但是前者采用的是
Add(...)
方法,该方法采用
Vehicle
(是
Car
的超类型)的参数。     

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

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-