当调用递归通用接口上的扩展方法时,是否会对struct实例进行装箱?

问题描述 投票:2回答:1

我有一个'递归通用接口':

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上运行?

我有理由相信两者:

  • x是盒装的:如果界面不是递归的话就是这种情况,所以这里可能就是这种情况,扩展方法需要一些MyInterface类型来操作,所以拳击发生
  • x未加框:参数T t直接表示结构MyStruct,因此它可以按原样传递而不需要装箱。

我真的很难想到一种方法来测试它,但我找不到一种方法来捕获结构被转换为接口类型的那一刻。

我为这个糟糕的标题道歉,但我不能说得更好,如果你能用更简单的术语描述问题,请随意编辑它。

c# generics polymorphism boxing
1个回答
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 method指令的'this'指针,用于method执行thisType
  • 如果thisType是一个值类型而thisType没有实现method那么ptr被解除引用,装箱,并作为'this'指针传递给callvirt method指令。

在这里,我们打了第一个案例,所以没有拳击发生。如果你打电话给t.GetHashCode()t.GetType(),那么JIT就会向t发出指令。

© www.soinside.com 2019 - 2024. All rights reserved.