今天又在看C#泛型的代码,又有一点小发现...但愿大家没听腻了我说泛型
是这样的,还是我上次写的那两个类:
interface ITest
{
void testMethod();
}
class Test<T> where T:ITest,new()
{
public Test()
{
t = new T();
}
public void foo()
{
t.testMethod();
}
private T t;
}
观察Test<T>.foo()函数的IL代码的时候,发现了一个很可疑的box操作:
.method public hidebysig instance void foo() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld !0 class ConsoleApplication1.Test<!0>::t
IL_0006: box !0
IL_000b: callvirt instance void ConsoleApplication1.ITest::testMethod()
IL_0010: ret
} // end of method Test::foo
说明一下,那个ConsoleApplication是namespace,!0 是IL中泛型参数T的占位符。
从上面的代码来看,泛型参数的实例 Test<T>::t,被当做一个ValueType,而且在调用相关方法的时候被box成了一个Constraint里定义的接口,然后使用了callvirt来调用接口的虚函数。如此一来,又有box,又有转型的动作,似乎调用方法的开销大了一点
写了一个实现同样功能的多态版本,进行对比:
class TestVirtual
{
public static void foo(ITest i)
{
i.testMethod();
}
}
相应的foo函数产生的IL代码如下:
.method public hidebysig static void foo(class ConsoleApplication1.ITest i) cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: callvirt instance void ConsoleApplication1.ITest::testMethod()
IL_0006: ret
} // end of method TestVirtual::foo
明显代价要小很多。
并且如果把上述泛型的IL代码改为:
.method public hidebysig instance void foo() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld !0 class ConsoleApplication1.Test<!0>::t
IL_0006: box !0
IL_000b: call instance void !0::testMethod()
IL_0010: ret
} // end of method Test::foo
结果会产生一个System.MissingMethodException,提示信息如下:
Unhandled Exception: System.MissingMethodException: Method not found: 'Void System.Object.testMethod()'.
从这里可以看出泛型参数的实例被box成了Object的实例,这到与隐含的泛型参数继承自Object 的Constraints吻合。看样子CLR的泛型支持充其量也就是一些底层的转型动作,而且还加上一个box的开销也不知道和java的泛型比,谁的开销更大一点了。
最后说一些题外话,看到Ninputer在抱怨不能约束ValueType或者ReferenceType,想到上次在C# team的blog里看到过这个事情,确实有准备添加这个功能啊,这里!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?