当调用递归泛型接口上的扩展方法时,结构实例是否会被装箱?

Do struct instances get boxed when an extension method on a recursive generic interface is called?

我有一个 'recursive generic interface':

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();
    }
}

然后我在结构中实现了接口:

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时,objectMyStruct x是装箱到MyInterface,还是扩展方法直接运行在 x?

我有理由相信两者:

我真的很努力地想出一种方法来测试它,但我找不到一种方法来捕捉结构被强制转换为接口类型的那一刻。

对于这个残酷的标题,我深表歉意,但我无法用更好的措辞表达,如果您能用更简单的术语描述问题,请随时编辑它。

没有拳击。

您可以通过将您的代码放入 sharplab here 来看到这一点。没有 box 说明。在某些情况下,可以 限制虚拟调用,但这里不行。

如果将结构转换为它的接口类型,那将框:

MyInterface x = new MyStruct();

但是,像这样调用一个泛型方法(无论泛型类型参数是否被约束为接口类型),并没有框。当您调用 MyExtensions.DoSomethingElse<MyStruct> 时,JIT 会发出该方法的新实现,它专门使用 MyStruct - 因此,不需要装箱。

MyExtensions.DoSomethingElse 执行 constrained virtual call 来调用 DoSomething 方法。这主要用于编译器在运行时不确定目标是值类型还是引用类型时,它基本上表示 "JIT, you figure this one out"。具体来说:

  • If thisType is a value type and thisType implements method then ptr is passed unmodified as the 'this' pointer to a call method instruction, for the implementation of method by thisType.
  • If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction.

在这里,我们遇到了第一种情况,因此没有装箱发生。如果您调用 t.GetHashCode()t.GetType(),那么 JIT 将向框 t.

发送指令