我有一个“递归通用接口”:
public interface MyInterface<T> where T : MyInterface<T>
{
T DoSomething();
}
我在上面定义了一个扩展方法:
public static class MyExtensions
{
public static T DoSomethingElse<T>(this T t)
where T : MyInterface<T>
{
Console.WriteLine("DoSomethingElse was called.");
return t.DoSomething();
}
}
然后我在struct中实现了接口:
public struct MyStruct : MyInterface<MyStruct>
{
public MyStruct DoSomething()
{
Console.WriteLine("DoSomething was called.");
return new MyStruct();
}
}
然后我在main中调用了扩展方法:
public class Program
{
static void Main(string[] args)
{
MyStruct x = new MyStruct();
MyStruct y = x.DoSomethingElse();
Console.ReadKey();
}
}
问题:调用DoSomethingElse
时,对象MyStruct x
是否装在MyInterface
中,还是扩展方法直接在x
上操作? / strong>
我有理由相信两者:
MyInterface
类型的东西才能进行装箱T t
直接表示结构MyStruct
,因此无需装箱即可直接传递。我非常努力地想出一种测试方法,但是找不到将结构转换为接口类型的那一刻。
我为这个残酷的标题表示歉意,但我说不出更好的话,如果您可以用更简单的术语来描述问题,请随时对其进行编辑。
答案 0 :(得分:3)
没有拳击。
通过将代码放入Sharplab here中,您可以看到这一点。没有box
指令。有一个受限制的虚拟呼叫,在某些情况下可以框显示,但不是在这里。
如果将结构转换为接口类型,则会出现以下对话框:
MyInterface x = new MyStruct();
但是,调用这样的泛型方法(无论泛型类型参数是否限于接口类型),都不会装箱。当您调用MyExtensions.DoSomethingElse<MyStruct>
时,JIT会发出该方法的新实现,它专门使用MyStruct
-因此,不需要装箱。
MyExtensions.DoSomethingElse
执行constrained virtual call来调用DoSomething
方法。它主要用于编译器不确定运行时目标是值类型还是引用类型时,它基本上说“ JIT,您可以弄清楚这一点”。具体来说:
- 如果
thisType
是一个值类型,并且thisType
实现了method
,那么ptr
会以未经修改的方式传递给指向call
{{1 }}指令,用于由method
实现method
。- 如果
thisType
是值类型,并且thisType
未实现thisType
,则将method
取消引用,装箱并作为指向{{ 1}}ptr
指令。
在这里,我们碰到了第一种情况,因此没有拳击发生。如果您调用了callvirt
或method
,则JIT会将指令发送到框t.GetHashCode()
。