我有一个'递归通用接口':
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
上运行?
我有理由相信两者:
MyInterface
类型来操作,所以拳击发生T t
直接表示结构MyStruct
,因此它可以按原样传递而不需要装箱。我真的很难想到一种方法来测试它,但我找不到一种方法来捕获结构被转换为接口类型的那一刻。
我为这个糟糕的标题道歉,但我不能说得更好,如果你能用更简单的术语描述问题,请随意编辑它。
没拳击。
您可以通过将代码放入sharplab here来查看。没有box
说明。有一个受限制的虚拟呼叫,在某些情况下可以装箱,但不是这里。
如果将结构转换为其接口类型,则将包含:
MyInterface x = new MyStruct();
但是,调用这样的泛型方法(无论泛型类型参数是否受限于接口类型)都不会。当你调用MyExtensions.DoSomethingElse<MyStruct>
时,JIT会发出一个新的方法实现,它专门使用MyStruct
- 因此,不需要装箱。
MyExtensions.DoSomethingElse
做了一个constrained virtual call来调用DoSomething
方法。这主要用于当编译器不确定目标在运行时是否为值或引用类型时,它基本上用“JIT,你想出这个”。特别:
- 如果
thisType
是一个值类型而thisType
实现method
那么ptr
未经修改地传递为call
method
指令的'this'指针,用于method
执行thisType
。- 如果
thisType
是一个值类型而thisType
没有实现method
那么ptr
被解除引用,装箱,并作为'this'指针传递给callvirt
method
指令。
在这里,我们打了第一个案例,所以没有拳击发生。如果你打电话给t.GetHashCode()
或t.GetType()
,那么JIT就会向t
发出指令。