递归通用接口上的扩展方法被调用时,结构实例会被装箱吗?

时间:2019-03-31 18:01:25

标签: c# generics polymorphism boxing

我有一个“递归通用接口”:

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>

我有理由相信两者:

  • x被装箱:如果接口不是递归的,则可能是这种情况,在这里可能就是这种情况,扩展方法需要使用MyInterface类型的东西才能进行装箱
  • x未装箱:参数T t直接表示结构MyStruct,因此无需装箱即可直接传递。

我非常努力地想出一种测试方法,但是找不到将结构转换为接口类型的那一刻。

我为这个残酷的标题表示歉意,但我说不出更好的话,如果您可以用更简单的术语来描述问题,请随时对其进行编辑。

1 个答案:

答案 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指令。
  •   

在这里,我们碰到了第一种情况,因此没有拳击发生。如果您调用了callvirtmethod,则JIT会将指令发送到框t.GetHashCode()