如果我们不依赖公认答案指出的 JIT 去虚拟化魔法,那么 唯一的 “官方方法”就是您猜测的选项 3:
// this shouldn't box but is pretty complicated just to call a method
void Test3() {
impl(ref this);
void impl<T>(ref T self) where T : IBar
=> self.Foo();
}
我们依靠 constrained
callvirt
IL 来帮助我们。
但它仍然与.NET 6 和 7 的基准测试代码相冲突:
// identical method bodies for all methods below
interface I {
int DefaultOne() {
var result = 0;
for (int i = 0; i < 100; i++) {
var a = i % 2;
var b = a * 4;
result += b;
}
return result;
}
int DefaultTwo() {
var result = 0;
for (int i = 0; i < 100; i++) {
var a = i % 2;
var b = a * 4;
result += b;
}
return result;
}
}
struct S : I {
public int DefaultTwo() {
var result = 0;
for (int i = 0; i < 100; i++) {
var a = i % 2;
var b = a * 4;
result += b;
}
return result;
}
}
通用约束和基准测试方法:
int BenchmarkDefaultOne() {
S str = default;
var result = DefaultOneGeneric(ref str);
return result;
}
int BenchmarkDefaultTwo() {
S str = default;
var result = DefaultTwoGeneric(ref str);
return result;
}
int DefaultOneGeneric<T>(ref T tparam) where T : I {
var result = 0;
for (int i = 0; i < 100; i++) {
result += tparam.DefaultOne();
}
return result;
}
int DefaultTwoGeneric<T>(ref T tparam) where T : I {
var result = 0;
for (int i = 0; i < 100; i++) {
result += tparam.DefaultTwo();
}
return result;
}
结果(仅限 AllocatedBytes)
BenchmarkDefaultOne 2,400
BenchmarkDefaultTwo 0
约束 constrained
方式(没有 JIT 帮助),这是可以预料到的
如果 thisType 是值类型并且 thisType 没有实现方法, 那么 ptr 将被 取消引用 , 装箱 ,并作为 'this' 指针传递给 callvirt 方法指令。
因此, 无法保证 通过我们的结构调用默认接口方法(未实现的方法)。