Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/263.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何实现虚拟泛型方法调用?_C#_.net_Clr - Fatal编程技术网

C# 如何实现虚拟泛型方法调用?

C# 如何实现虚拟泛型方法调用?,c#,.net,clr,C#,.net,Clr,我对CLR如何实现这样的调用很感兴趣: abstract class A { public abstract void Foo<T, U, V>(); } A a = ... a.Foo<int, string, decimal>(); // <=== ? 抽象类A{ 公开摘要void Foo(); } A=。。。 a、 Foo();// 对。特定类型的代码由CLR在运行时生成,并保留实现的哈希表(或类似的) 第372页,共页: 当使用泛型类型的方法 参

我对CLR如何实现这样的调用很感兴趣:

abstract class A {
    public abstract void Foo<T, U, V>();
}

A a = ...
a.Foo<int, string, decimal>(); // <=== ?
抽象类A{
公开摘要void Foo();
}
A=。。。

a、 Foo();// 对。特定类型的代码由CLR在运行时生成,并保留实现的哈希表(或类似的)

第372页,共页:

当使用泛型类型的方法 参数是JIT编译的,CLR 获取方法的IL,替换 指定类型参数,然后 创建特定的本机代码 该方法在计算机上运行 指定的数据类型。这正是 你想要的是什么,是主要的 泛型的特征。但是, 这样做的一个缺点是:CLR保持 为每个 方法/类型组合。这是 称为代码爆炸。这 最终可能会增加成本 应用程序的工作集 实质上,因此造成伤害 演出 幸运的是,CLR有一些 内置优化以减少 代码爆炸。首先,如果方法是 为特定类型参数调用, 然后,再次调用该方法 使用相同的类型参数,CLR 将为此编译代码 方法/类型组合只需一次。So 如果一个程序集使用列表, 和一个完全不同的组件 (加载在同一AppDomain中)也 使用列表时,CLR将 编译列表的方法 就一次。这减少了代码爆炸 实质上


我没有找到多少关于这一点的确切信息,所以大部分答案都是基于(甚至在.Net 1.0问世之前!)、中的一条简短注释以及我从中收集的信息(尽管我无法找到调用虚拟泛型方法的确切代码)

让我们从简单开始:如何调用非泛型非虚方法?通过直接调用方法代码,使编译后的代码包含直接地址。编译器从方法表中获取方法地址(见下一段)。能这么简单吗?嗯,差不多了。方法是JIT的这一事实使它变得有点复杂:实际调用的是编译方法的代码,如果它还没有编译,则只执行它;或者它是一条直接调用已编译代码的指令,如果它已经存在的话。我将进一步忽略这个细节

现在,如何调用非泛型虚拟方法?类似于C++语言中的多态性,有一个方法表可从<代码>这个指针(引用)访问。每个派生类都有自己的方法表及其方法。因此,要调用虚拟方法,请获取对
this
(作为参数传入)的引用,然后获取对方法表的引用,查看其中的正确条目(对于特定函数,条目号是常量),并调用条目点所指向的代码。通过接口调用方法稍微复杂一些,但现在对我们来说并不有趣

现在我们需要了解代码共享。如果类型参数中的引用类型对应于任何其他引用类型,并且值类型完全相同,则代码可以在同一方法的两个“实例”之间共享。因此,例如
C.M()
C.M()
共享代码,而不是与
C.M()
共享代码。类型参数和方法类型参数之间没有区别。(2001年的原始论文提到,当两个参数都是布局相同的
struct
s时,代码也可以共享,但我不确定在实际实现中是否如此。)

让我们在通往泛型方法的道路上迈出中间的一步:泛型类型中的非泛型方法。由于代码共享,我们需要从某处获取类型参数(例如,用于调用代码,如
newt[]
)。因此,泛型类型的每个实例化(例如
C
C
)都有自己的类型句柄,其中包含类型参数和方法表。普通方法可以从
引用访问此类型句柄(技术上称为
MethodTable
,尽管它包含的不仅仅是方法表)。有两种方法不能做到这一点:静态方法和值类型方法。对于这些类型,类型句柄作为隐藏参数传入

对于非虚拟泛型方法,类型句柄不够,因此它们会得到不同的隐藏参数,
MethodDesc
,其中包含类型参数。此外,编译器不能将实例化存储在普通方法表中,因为这是静态的。因此,它为泛型方法创建了另一个不同的方法表,该表由类型参数索引,并从中获取方法地址(如果它已经存在并具有兼容的类型参数),或者创建一个新条目

虚拟泛型方法现在很简单:编译器不知道具体的类型,所以它必须在运行时使用方法表。并且普通方法表不能使用,因此它必须在特殊方法表中查找泛型方法。当然,包含类型参数的隐藏参数仍然存在

在研究这一点时,我们学到了一个有趣的小道消息:因为JITer非常懒惰,所以下面的代码(完全无用)可以工作:

object Lift<T>(int count) where T : new()
{
    if (count == 0)
        return new T();

    return Lift<List<T>>(count - 1);
}
对象提升(int计数),其中T:new()
{
如果(计数=0)
返回新的T();
返回升降机(计数-1);
}

等效的C++代码导致编译器放弃堆栈溢出。

< P> <强>编辑< /强> < /P> 我现在遇到了,我现在遇到了,它清楚地表明,泛型在使用引用类型时正在重用相同的代码,因此我将接受它作为最终权威

原始答案

我在这里看到了两个不一致的答案,并且都提到了他们的立场,所以我将尝试添加我的两分钱

首先,微软Pres出版的Jeffrey Richter通过C#实现Clr